From 86914b7fcc652dae2ba9f40b78492a5653ba00f5 Mon Sep 17 00:00:00 2001 From: l-proger Date: Tue, 13 May 2025 02:46:21 +0300 Subject: [PATCH] * --- LifeAudioPlayer/Data.pak | 0 LifeAudioPlayer/LifeAudioPlayer.cpp | 70 + LifeAudioPlayer/LifeAudioPlayer.vcxproj | 171 + LifeAudioPlayer/stdafx.cpp | 8 + LifeAudioPlayer/stdafx.h | 13 + LifeAudioPlayer/targetver.h | 8 + LifeCore/Array.cpp | 0 LifeCore/Array.h | 18 + LifeCore/Axis.h | 11 + LifeCore/BoundingBox3D.h | 170 + LifeCore/Build.h | 10 + LifeCore/Com.h | 22 + LifeCore/Delegate.h | 132 + LifeCore/DisplayMode.h | 14 + LifeCore/Error.h | 20 + LifeCore/FrameCounter.cpp | 78 + LifeCore/FrameCounter.h | 32 + LifeCore/ICamera.h | 14 + LifeCore/IDataRectangle.h | 14 + LifeCore/IDataStream.h | 24 + LifeCore/IDevice.h | 9 + LifeCore/IImage.h | 14 + LifeCore/IInputSystem.cpp | 61 + LifeCore/IInputSystem.h | 132 + LifeCore/IMemoryStream.h | 139 + LifeCore/IProperty.cpp | 24 + LifeCore/IProperty.h | 57 + LifeCore/IPropertyHolder.cpp | 66 + LifeCore/IPropertyHolder.h | 27 + LifeCore/IPropertyItem.h | 25 + LifeCore/ISerializable.h | 13 + LifeCore/ITransform.h | 114 + LifeCore/IWindow.h | 43 + LifeCore/IWindowDemander.h | 10 + LifeCore/InputDevice.h | 11 + LifeCore/InputListener222.h | 17 + LifeCore/InputSystem.cpp | 101 + LifeCore/InputSystem.h | 19 + LifeCore/InputSystem222.h | 22 + LifeCore/InputSystemOIS22.cpp | 181 + LifeCore/InputSystemOIS22.h | 30 + LifeCore/InputTypes222.h | 182 + LifeCore/LString.cpp | 209 + LifeCore/LString.h | 75 + LifeCore/LifeCore.vcxproj | 299 + LifeCore/PixelBuffer.cpp | 10 + LifeCore/PixelBuffer.h | 21 + LifeCore/Property.h | 183 + LifeCore/Range.h | 20 + LifeCore/RawBuffer.h | 30 + LifeCore/Singleton.h | 27 + LifeCore/Timer.cpp | 60 + LifeCore/Timer.h | 31 + LifeCore/Types.h | 36 + LifeCore/Utils.cpp | 221 + LifeCore/Utils.h | 63 + LifeCore/Window.h | 36 + LifeCore/WindowPixelManager.cpp | 34 + LifeCore/WindowPixelManager.h | 20 + LifeCore/WindowWin.cpp | 548 + LifeCore/WindowWin.h | 74 + LifeCore/delegate/Delegate.h | 125 + LifeCore/delegate/Delegate_base.h | 164 + LifeCore/log/CLog.cpp | 33 + LifeCore/log/CLog.h | 28 + LifeCore/log/NLog.cpp | 94 + LifeCore/log/NLog.h | 55 + LifeCore/math/LifeMath.cpp | 125 + LifeCore/math/LifeMath.h | 1428 +++ LifeCore/math/Matrix4x4.h | 274 + LifeCore/math/Plane.h | 96 + LifeCore/math/Quaternion.h | 93 + LifeCore/math/Rectangle.h | 127 + LifeCore/math/Spline.h | 22 + LifeCore/math/Vector2D.h | 175 + LifeCore/math/Vector3D.h | 180 + LifeCore/math/Vector4D.h | 192 + LifeCore/math/vector_base.h | 17 + LifeCore/media/WebCamera.cpp | 225 + LifeCore/media/WebCamera.h | 54 + LifeCore/media/WinMfFormatReader.cpp | 461 + LifeCore/media/WinMfFormatReader.h | 21 + LifeCore/media/WinMfImageGrabber.cpp | 342 + LifeCore/media/WinMfImageGrabber.h | 65 + LifeCore/media/WinMfMediaType.h | 129 + LifeCore/minizip/crypt.h | 129 + LifeCore/minizip/ioapi.c | 230 + LifeCore/minizip/ioapi.h | 190 + LifeCore/minizip/iowin32.c | 380 + LifeCore/minizip/iowin32.h | 27 + LifeCore/minizip/miniunz.c | 638 ++ LifeCore/minizip/minizip.c | 485 + LifeCore/minizip/mztools.c | 276 + LifeCore/minizip/mztools.h | 31 + LifeCore/minizip/unzip.c | 2077 ++++ LifeCore/minizip/unzip.h | 426 + LifeCore/minizip/zip.c | 1974 ++++ LifeCore/minizip/zip.h | 356 + LifeCore/tools/CompilerDetect.h | 50 + LifeCore/tools/Inline.h | 18 + LifeCore/tools/OsDetect.h | 26 + LifeCore/vfs/IContentProvider.h | 22 + LifeCore/vfs/IFile.h | 52 + LifeCore/vfs/IFileSystem.cpp | 158 + LifeCore/vfs/IFileSystem.h | 64 + LifeCore/vfs/IFolder.h | 120 + LifeCore/vfs/RootFolder.h | 19 + LifeCore/vfs/WinFileStream.cpp | 57 + LifeCore/vfs/WinFileStream.h | 25 + LifeCore/vfs/fs_win/IFSContentProvider.cpp | 68 + LifeCore/vfs/fs_win/IFSContentProvider.h | 26 + LifeCore/vfs/fs_win/IFSFile.cpp | 62 + LifeCore/vfs/fs_win/IFSFile.h | 29 + LifeCore/vfs/fs_win/IFSFolder.cpp | 53 + LifeCore/vfs/fs_win/IFSFolder.h | 26 + LifeCore/vfs/fs_win/IFSStream.h | 86 + LifeCore/vfs/pak/IPakFile.cpp | 31 + LifeCore/vfs/pak/IPakFile.h | 26 + LifeCore/vfs/pak/IPakFileContentProvider.cpp | 76 + LifeCore/vfs/pak/IPakFileContentProvider.h | 28 + LifeCore/vfs/pak/IPakFolder.cpp | 31 + LifeCore/vfs/pak/IPakFolder.h | 24 + LifeCore/vfs/pak/IPakStream.h | 77 + LifeCore/vfs/pak/pak_io/EntryInfo.h | 13 + LifeCore/vfs/pak/pak_io/FileEntry.h | 11 + LifeCore/vfs/pak/pak_io/FolderEntry.h | 6 + LifeCore/vfs/pak/pak_io/PakFile.cpp | 312 + LifeCore/vfs/pak/pak_io/PakFile.h | 80 + LifeCore/vfs/pak/pak_io/PakTypes.h | 16 + LifeCore/vfs/pak/pak_io/pakHeader.h | 12 + LifeCore/vfs/pak/pak_io/targetver.h | 12 + LifeCore/vfs/zip/IZipFile.cpp | 41 + LifeCore/vfs/zip/IZipFile.h | 27 + LifeCore/vfs/zip/IZipFileContentProvider.cpp | 83 + LifeCore/vfs/zip/IZipFileContentProvider.h | 130 + LifeCore/vfs/zip/IZipFolder.cpp | 31 + LifeCore/vfs/zip/IZipFolder.h | 25 + LifeCore/vfs/zip/IZipStream.h | 108 + LifeExport/Exports.def | 9 + LifeExport/LifeExport.cpp | 163 + LifeExport/LifeExport.h | 6 + LifeExport/LifeExport.vcxproj | 197 + LifeExport/dllmain.cpp | 0 LifeExport/stdafx.cpp | 8 + LifeExport/stdafx.h | 14 + LifeExport/targetver.h | 8 + LifeExportInternal/Exports.def | 5 + LifeExportInternal/LifeExportInternal.cpp | 81 + LifeExportInternal/LifeExportInternal.h | 11 + LifeExportInternal/LifeExportInternal.vcxproj | 190 + LifeExportInternal/ReadMe.txt | 40 + LifeExportInternal/dllmain.cpp | 18 + LifeExportInternal/stdafx.cpp | 8 + LifeExportInternal/stdafx.h | 14 + LifeExportInternal/targetver.h | 8 + LifeGUI/LifeGUI.vcxproj | 251 + LifeGUI/src/GUIBorder.h | 50 + LifeGUI/src/GUIButton.cpp | 152 + LifeGUI/src/GUIButton.h | 37 + LifeGUI/src/GUICheckBox.cpp | 110 + LifeGUI/src/GUICheckBox.h | 32 + LifeGUI/src/GUIComboBox.cpp | 114 + LifeGUI/src/GUIComboBox.h | 30 + LifeGUI/src/GUIFileBrowser.cpp | 142 + LifeGUI/src/GUIFileBrowser.h | 38 + LifeGUI/src/GUIFileBrowserDialog.cpp | 34 + LifeGUI/src/GUIFileBrowserDialog.h | 24 + LifeGUI/src/GUIFont.cpp | 137 + LifeGUI/src/GUIFont.h | 37 + LifeGUI/src/GUIFontGlyph.h | 20 + LifeGUI/src/GUIFontManager.cpp | 8 + LifeGUI/src/GUIFontManager.h | 22 + LifeGUI/src/GUIForm.cpp | 85 + LifeGUI/src/GUIForm.h | 29 + LifeGUI/src/GUIList.cpp | 85 + LifeGUI/src/GUIList.h | 37 + LifeGUI/src/GUIManager.cpp | 440 + LifeGUI/src/GUIManager.h | 67 + LifeGUI/src/GUIMenu.cpp | 237 + LifeGUI/src/GUIMenu.h | 46 + LifeGUI/src/GUIMenuItem.cpp | 26 + LifeGUI/src/GUIMenuItem.h | 26 + LifeGUI/src/GUIMenuTextItem.h | 16 + LifeGUI/src/GUIMessaging.h | 59 + LifeGUI/src/GUIOverlay.cpp | 330 + LifeGUI/src/GUIOverlay.h | 71 + LifeGUI/src/GUIOverlayManager.cpp | 77 + LifeGUI/src/GUIOverlayManager.h | 38 + LifeGUI/src/GUIPicture.cpp | 135 + LifeGUI/src/GUIPicture.h | 32 + LifeGUI/src/GUIPlane.cpp | 181 + LifeGUI/src/GUIPlane.h | 37 + LifeGUI/src/GUIProgress.cpp | 65 + LifeGUI/src/GUIProgress.h | 23 + LifeGUI/src/GUIPropertyGrid.cpp | 150 + LifeGUI/src/GUIPropertyGrid.h | 134 + LifeGUI/src/GUIRadioButton.cpp | 111 + LifeGUI/src/GUIRadioButton.h | 30 + LifeGUI/src/GUIRenderWindow.cpp | 82 + LifeGUI/src/GUIRenderWindow.h | 46 + LifeGUI/src/GUIScroll.cpp | 310 + LifeGUI/src/GUIScroll.h | 55 + LifeGUI/src/GUIScrollablePlane.cpp | 206 + LifeGUI/src/GUIScrollablePlane.h | 50 + LifeGUI/src/GUISlider.cpp | 223 + LifeGUI/src/GUISlider.h | 48 + LifeGUI/src/GUISplitPanel.cpp | 244 + LifeGUI/src/GUISplitPanel.h | 48 + LifeGUI/src/GUITab.cpp | 100 + LifeGUI/src/GUITab.h | 40 + LifeGUI/src/GUITabPage.h | 16 + LifeGUI/src/GUIText.cpp | 311 + LifeGUI/src/GUIText.h | 72 + LifeGUI/src/GUITextInput.cpp | 153 + LifeGUI/src/GUITextInput.h | 34 + LifeGUI/src/GUIToolStrip.cpp | 63 + LifeGUI/src/GUIToolStrip.h | 27 + LifeGUI/src/GUIToolStripButton.cpp | 20 + LifeGUI/src/GUIToolStripButton.h | 17 + LifeGUI/src/GUIToolStripImage.cpp | 15 + LifeGUI/src/GUIToolStripImage.h | 15 + LifeGUI/src/GUIToolStripItem.cpp | 0 LifeGUI/src/GUIToolStripItem.h | 17 + LifeGUI/src/GUIToolStripMenu.cpp | 60 + LifeGUI/src/GUIToolStripMenu.h | 26 + LifeGUI/src/GUITreeNode.cpp | 19 + LifeGUI/src/GUITreeNode.h | 82 + LifeGUI/src/GUITreeView.cpp | 127 + LifeGUI/src/GUITreeView.h | 42 + LifeGUI/src/GUIVertex.h | 64 + LifeGUI/src/GUIWidgetSkin.h | 96 + LifeGUI/src/GUIWindow.cpp | 91 + LifeGUI/src/GUIWindow.h | 607 ++ LifeGUI/src/IGUICursor.h | 19 + LifeGUI/src/IGUIDataManager.h | 20 + LifeGUI/src/IGUIRenderer.cpp | 46 + LifeGUI/src/IGUIRenderer.h | 45 + LifeGUI/src/IImage.h | 28 + LifeGUI/src/PropertyItemFile.h | 43 + LifeGUI/src/PropertyItemFloat.h | 32 + LifeGame/LifeGame.vcxproj | 211 + LifeGame/src/Action.cpp | 28 + LifeGame/src/Action.h | 37 + LifeGame/src/ActionSystem.cpp | 33 + LifeGame/src/ActionSystem.h | 28 + LifeGame/src/BulletLibs.h | 12 + LifeGame/src/CGUIDataManager.cpp | 21 + LifeGame/src/CGUIDataManager.h | 17 + LifeGame/src/CGUIRenderer.cpp | 121 + LifeGame/src/CGUIRenderer.h | 31 + LifeGame/src/CImage.cpp | 64 + LifeGame/src/CImage.h | 34 + LifeGame/src/Entity.cpp | 114 + LifeGame/src/Entity.h | 93 + LifeGame/src/EntityFactory.h | 125 + LifeGame/src/GOCCameraFree.cpp | 110 + LifeGame/src/GOCCameraFree.h | 23 + LifeGame/src/GOCLogic.h | 15 + LifeGame/src/GOComponent.h | 14 + LifeGame/src/GUIOutput.h | 44 + LifeGame/src/Game.cpp | 39 + LifeGame/src/Game.h | 167 + LifeGame/src/GameCamera.h | 37 + LifeGame/src/GameGlobals.h | 4 + LifeGame/src/Graphics.h | 22 + LifeGame/src/IActionListener.h | 15 + LifeGame/src/IActionProvider.h | 13 + LifeGame/src/IGraphicsEntity.cpp | 36 + LifeGame/src/IGraphicsEntity.h | 19 + LifeGame/src/IPhysicsEntity.cpp | 344 + LifeGame/src/IPhysicsEntity.h | 151 + LifeGame/src/IPhysicsWorld.h | 228 + LifeGame/src/IScriptEntity.h | 8 + LifeGame/src/InputActionProvider.cpp | 75 + LifeGame/src/InputActionProvider.h | 52 + LifeGame/src/World.cpp | 97 + LifeGame/src/World.h | 48 + LifeGame/src/script/Script.cpp | 64 + LifeGame/src/script/Script.h | 25 + LifeGame/src/script/ScriptSystem.cpp | 105 + LifeGame/src/script/ScriptSystem.h | 26 + LifeGraphics/AnimationController.h | 118 + LifeGraphics/AnimationFrame.h | 39 + LifeGraphics/AnimationTrack.h | 165 + LifeGraphics/Bone.h | 47 + LifeGraphics/BoneTransform.h | 65 + LifeGraphics/BoundingSphere.h | 19 + LifeGraphics/CG.cpp | 24 + LifeGraphics/CG.h | 23 + LifeGraphics/Camera.cpp | 28 + LifeGraphics/CustomSwapChain.cpp | 45 + LifeGraphics/CustomSwapChain.h | 28 + LifeGraphics/DayTime.h | 137 + LifeGraphics/GeometrySubset.h | 20 + LifeGraphics/Globals.h | 53 + LifeGraphics/IGraphicsNode.h | 102 + LifeGraphics/IMesh.h | 88 + LifeGraphics/IObject.h | 87 + LifeGraphics/IObjectMesh.h | 184 + LifeGraphics/IRenderOutput.cpp | 15 + LifeGraphics/IRenderOutput.h | 20 + LifeGraphics/IResource.h | 37 + LifeGraphics/IResourceCollection.h | 88 + LifeGraphics/IScene.h | 1203 +++ LifeGraphics/ISkinnedMesh.h | 11 + LifeGraphics/ISwapChain.h | 23 + LifeGraphics/LifeDebug.h | 24 + LifeGraphics/LifeEngine.config | 1 + LifeGraphics/LifeEngine.cpp | 85 + LifeGraphics/LifeEngine.h | 56 + LifeGraphics/LifeGraphics.vcxproj | 561 + LifeGraphics/Light.h | 204 + LifeGraphics/MeshManager.cpp | 19 + LifeGraphics/MeshManager.h | 19 + LifeGraphics/PassGBuffer.h | 18 + LifeGraphics/Plugin.cpp | 34 + LifeGraphics/Plugin.h | 43 + LifeGraphics/PluginServer.cpp | 46 + LifeGraphics/PluginServer.h | 35 + LifeGraphics/Rectangle2D.h | 41 + LifeGraphics/RenderOperation.h | 21 + LifeGraphics/RenderPass.h | 10 + LifeGraphics/RenderQueue.h | 33 + LifeGraphics/ResourceManager.cpp | 639 ++ LifeGraphics/ResourceManager.h | 104 + LifeGraphics/ShadowMapManager.h | 206 + LifeGraphics/Skeleton.h | 117 + LifeGraphics/SkinInfo.h | 23 + LifeGraphics/Sky.h | 397 + LifeGraphics/Terrain.h | 924 ++ LifeGraphics/TypesVideo.h | 12 + LifeGraphics/Vertex.h | 64 + LifeGraphics/XML.h | 22 + LifeGraphics/camera/Camera.h | 329 + LifeGraphics/camera/Frustum.h | 186 + LifeGraphics/constants.h | 60 + LifeGraphics/debug/IDebugDrawer.h | 231 + LifeGraphics/passPSSM.h | 455 + LifeGraphics/pugiconfig.hpp | 62 + LifeGraphics/pugixml.cpp | 9574 +++++++++++++++++ LifeGraphics/pugixml.hpp | 1131 ++ LifeGraphics/pugixml/contrib/foreach.hpp | 102 + LifeGraphics/pugixml/docs/images/caution.png | Bin 0 -> 426 bytes LifeGraphics/pugixml/docs/images/dom_tree.png | Bin 0 -> 8182 bytes .../pugixml/docs/images/dom_tree_thumb.png | Bin 0 -> 3127 bytes LifeGraphics/pugixml/docs/images/home.png | Bin 0 -> 217 bytes LifeGraphics/pugixml/docs/images/next.png | Bin 0 -> 204 bytes LifeGraphics/pugixml/docs/images/note.png | Bin 0 -> 357 bytes LifeGraphics/pugixml/docs/images/prev.png | Bin 0 -> 198 bytes LifeGraphics/pugixml/docs/images/up.png | Bin 0 -> 224 bytes .../pugixml/docs/images/vs2005_link1.png | Bin 0 -> 25698 bytes .../docs/images/vs2005_link1_thumb.png | Bin 0 -> 2531 bytes .../pugixml/docs/images/vs2005_link2.png | Bin 0 -> 18063 bytes .../docs/images/vs2005_link2_thumb.png | Bin 0 -> 1901 bytes .../pugixml/docs/images/vs2005_pch1.png | Bin 0 -> 27693 bytes .../pugixml/docs/images/vs2005_pch1_thumb.png | Bin 0 -> 4511 bytes .../pugixml/docs/images/vs2005_pch2.png | Bin 0 -> 14895 bytes .../pugixml/docs/images/vs2005_pch2_thumb.png | Bin 0 -> 1579 bytes .../pugixml/docs/images/vs2005_pch3.png | Bin 0 -> 15931 bytes .../pugixml/docs/images/vs2005_pch3_thumb.png | Bin 0 -> 1944 bytes .../pugixml/docs/images/vs2005_pch4.png | Bin 0 -> 15117 bytes .../pugixml/docs/images/vs2005_pch4_thumb.png | Bin 0 -> 1632 bytes .../pugixml/docs/images/vs2010_link1.png | Bin 0 -> 19609 bytes .../docs/images/vs2010_link1_thumb.png | Bin 0 -> 1765 bytes .../pugixml/docs/images/vs2010_link2.png | Bin 0 -> 17206 bytes .../docs/images/vs2010_link2_thumb.png | Bin 0 -> 1820 bytes LifeGraphics/pugixml/docs/manual.html | 205 + LifeGraphics/pugixml/docs/manual/access.html | 723 ++ LifeGraphics/pugixml/docs/manual/apiref.html | 1396 +++ LifeGraphics/pugixml/docs/manual/changes.html | 785 ++ LifeGraphics/pugixml/docs/manual/dom.html | 682 ++ LifeGraphics/pugixml/docs/manual/install.html | 430 + LifeGraphics/pugixml/docs/manual/loading.html | 853 ++ LifeGraphics/pugixml/docs/manual/modify.html | 553 + LifeGraphics/pugixml/docs/manual/saving.html | 463 + LifeGraphics/pugixml/docs/manual/toc.html | 153 + LifeGraphics/pugixml/docs/manual/xpath.html | 742 ++ LifeGraphics/pugixml/docs/pugixml.css | 598 + LifeGraphics/pugixml/docs/quickstart.html | 845 ++ .../pugixml/docs/samples/character.xml | 8 + .../docs/samples/custom_memory_management.cpp | 27 + LifeGraphics/pugixml/docs/samples/include.cpp | 64 + .../docs/samples/load_error_handling.cpp | 31 + .../pugixml/docs/samples/load_file.cpp | 16 + .../pugixml/docs/samples/load_memory.cpp | 64 + .../pugixml/docs/samples/load_options.cpp | 30 + .../pugixml/docs/samples/load_stream.cpp | 97 + .../pugixml/docs/samples/modify_add.cpp | 29 + .../pugixml/docs/samples/modify_base.cpp | 43 + .../pugixml/docs/samples/modify_remove.cpp | 27 + .../docs/samples/save_custom_writer.cpp | 116 + .../pugixml/docs/samples/save_file.cpp | 17 + .../pugixml/docs/samples/save_file_output.xml | 2 + .../pugixml/docs/samples/save_options.cpp | 48 + .../pugixml/docs/samples/save_stream.cpp | 18 + .../pugixml/docs/samples/save_subtree.cpp | 26 + .../pugixml/docs/samples/transitions.xml | 7 + .../pugixml/docs/samples/traverse_base.cpp | 51 + .../pugixml/docs/samples/traverse_iter.cpp | 27 + .../docs/samples/traverse_predicate.cpp | 48 + .../pugixml/docs/samples/traverse_walker.cpp | 35 + LifeGraphics/pugixml/docs/samples/tree.xml | 12 + .../pugixml/docs/samples/weekly-shift_jis.xml | 78 + .../pugixml/docs/samples/weekly-utf-16.xml | Bin 0 -> 3186 bytes .../pugixml/docs/samples/weekly-utf-8.xml | 78 + .../pugixml/docs/samples/xgconsole.xml | 12 + .../pugixml/docs/samples/xpath_error.cpp | 43 + .../pugixml/docs/samples/xpath_query.cpp | 36 + .../pugixml/docs/samples/xpath_select.cpp | 27 + .../pugixml/docs/samples/xpath_variables.cpp | 38 + LifeGraphics/pugixml/lib/pugixml_s.lib | Bin 0 -> 1025068 bytes LifeGraphics/pugixml/lib/pugixml_s.pdb | Bin 0 -> 266240 bytes LifeGraphics/pugixml/lib/pugixml_sd.lib | Bin 0 -> 1318866 bytes LifeGraphics/pugixml/lib/pugixml_sd.pdb | Bin 0 -> 274432 bytes LifeGraphics/pugixml/readme.txt | 52 + LifeGraphics/pugixml/scripts/CMakeLists.txt | 5 + LifeGraphics/pugixml/scripts/premake4.lua | 86 + .../scripts/pugixml.xcodeproj/project.pbxproj | 213 + .../pugixml/scripts/pugixml_codeblocks.cbp | 44 + .../pugixml/scripts/pugixml_codelite.project | 56 + .../pugixml/scripts/pugixml_vs2005.vcproj | 343 + .../scripts/pugixml_vs2005_static.vcproj | 343 + .../pugixml/scripts/pugixml_vs2008.vcproj | 339 + .../scripts/pugixml_vs2008_static.vcproj | 339 + .../pugixml/scripts/pugixml_vs2010.vcxproj | 161 + .../pugixml/scripts/pugixml_vs2010_static.sln | 26 + .../pugixml/scripts/pugixml_vs2010_static.suo | Bin 0 -> 9728 bytes .../scripts/pugixml_vs2010_static.vcxproj | 163 + .../pugixml_vs2010_static.vcxproj.user | 3 + .../scripts/vs2010/x32/Debug/CL.read.1.tlog | Bin 0 -> 9398 bytes .../scripts/vs2010/x32/Debug/CL.write.1.tlog | Bin 0 -> 274 bytes .../vs2010/x32/Debug/Lib-link.read.1.tlog | 1 + .../vs2010/x32/Debug/Lib-link.write.1.tlog | Bin 0 -> 210 bytes .../scripts/vs2010/x32/Debug/Lib.read.1.tlog | Bin 0 -> 378 bytes .../vs2010/x32/Debug/cl.command.1.tlog | Bin 0 -> 556 bytes .../vs2010/x32/Debug/lib.command.1.tlog | Bin 0 -> 252 bytes .../vs2010/x32/Debug/pugixml.lastbuildstate | 2 + .../scripts/vs2010/x32/Debug/pugixml.obj | Bin 0 -> 1150258 bytes .../x32/Debug/pugixml_vs2010_static.log | 18 + .../scripts/vs2010/x32/Release/CL.read.1.tlog | Bin 0 -> 9254 bytes .../vs2010/x32/Release/CL.write.1.tlog | Bin 0 -> 276 bytes .../vs2010/x32/Release/Lib-link.read.1.tlog | 1 + .../vs2010/x32/Release/Lib-link.write.1.tlog | Bin 0 -> 212 bytes .../vs2010/x32/Release/Lib.read.1.tlog | Bin 0 -> 386 bytes .../vs2010/x32/Release/cl.command.1.tlog | Bin 0 -> 552 bytes .../vs2010/x32/Release/lib.command.1.tlog | Bin 0 -> 258 bytes .../vs2010/x32/Release/pugixml.lastbuildstate | 2 + .../scripts/vs2010/x32/Release/pugixml.obj | Bin 0 -> 875422 bytes .../x32/Release/pugixml_vs2010_static.log | 18 + .../pugixml/scripts/vs2010/x32/pugixml_s.lib | Bin 0 -> 1025068 bytes .../pugixml/scripts/vs2010/x32/pugixml_s.pdb | Bin 0 -> 266240 bytes .../pugixml/scripts/vs2010/x32/pugixml_sd.lib | Bin 0 -> 1318866 bytes .../pugixml/scripts/vs2010/x32/pugixml_sd.pdb | Bin 0 -> 274432 bytes LifeGraphics/pugixml/src/pugiconfig.hpp | 62 + LifeGraphics/pugixml/src/pugixml.cpp | 9574 +++++++++++++++++ LifeGraphics/pugixml/src/pugixml.hpp | 1131 ++ LifeGraphics/queries/IFrustumQuery.cpp | 13 + LifeGraphics/queries/IFrustumQuery.h | 29 + LifeGraphics/queries/INodeQuery.h | 31 + LifeGraphics/queries/IQuery.h | 13 + LifeGraphics/render/Enums.h | 182 + LifeGraphics/render/IBlendState.h | 19 + LifeGraphics/render/IConstantBuffer.h | 16 + LifeGraphics/render/IConstantMapping.cpp | 45 + LifeGraphics/render/IConstantMapping.h | 38 + LifeGraphics/render/IDataBuffer.h | 30 + LifeGraphics/render/IDepthStencilState.h | 17 + LifeGraphics/render/IIndexBuffer.h | 17 + LifeGraphics/render/IInputElement.h | 44 + LifeGraphics/render/IInputLayout.h | 30 + LifeGraphics/render/IMacroDefine.h | 25 + LifeGraphics/render/IMaterial.cpp | 232 + LifeGraphics/render/IMaterial.h | 100 + LifeGraphics/render/IMaterialProxy.cpp | 92 + LifeGraphics/render/IMaterialProxy.h | 50 + LifeGraphics/render/IRasterizerState.h | 25 + LifeGraphics/render/IRenderTargetView.h | 13 + LifeGraphics/render/IRenderer.cpp | 12 + LifeGraphics/render/IRenderer.h | 199 + LifeGraphics/render/IShader.h | 36 + LifeGraphics/render/IShaderResourceView.h | 17 + LifeGraphics/render/ISurface.h | 12 + LifeGraphics/render/ITexture2D.h | 37 + LifeGraphics/render/ITextureContainer.h | 33 + LifeGraphics/render/ITextureCube.h | 19 + LifeGraphics/render/ITextureDescription.h | 13 + LifeGraphics/render/ITextureSampler.h | 79 + LifeGraphics/render/IVertexBuffer.h | 20 + LifeGraphics/render/IVideoTexture.h | 88 + LifeGraphics/render/PostDOF.h | 86 + LifeGraphics/render/PostEffect.h | 30 + LifeGraphics/render/PostHDR.h | 420 + LifeGraphics/render/PostSSAO.cpp | 101 + LifeGraphics/render/PostSSAO.h | 39 + LifeGraphics/render/PosteffectManager.h | 56 + LifeGraphics/render/RenderParams.h | 22 + LifeGraphics/render/RendererDX10.cpp | 80 + LifeGraphics/render/RendererDX10.h | 10 + LifeGraphics/render/RendererSoftware.h | 183 + LifeGraphics/render/dx11/IBlendState11.h | 33 + .../render/dx11/IConstantBuffer11.cpp | 12 + LifeGraphics/render/dx11/IConstantBuffer11.h | 61 + .../render/dx11/IDepthStencilState11.h | 31 + LifeGraphics/render/dx11/IIndexBuffer11.h | 98 + LifeGraphics/render/dx11/IInputLayout11.h | 23 + LifeGraphics/render/dx11/IPixelShader11.h | 109 + LifeGraphics/render/dx11/IRasterizerState11.h | 62 + .../render/dx11/IRenderTargetViewDX11.h | 27 + .../render/dx11/IShaderResourceView11.h | 26 + LifeGraphics/render/dx11/ISurface11.cpp | 33 + LifeGraphics/render/dx11/ISurface11.h | 31 + LifeGraphics/render/dx11/ISwapChain11.cpp | 85 + LifeGraphics/render/dx11/ISwapChain11.h | 24 + LifeGraphics/render/dx11/ITexture2D11.h | 325 + LifeGraphics/render/dx11/ITextureSampler11.h | 36 + LifeGraphics/render/dx11/IVertexBuffer11.h | 109 + LifeGraphics/render/dx11/IVertexShader11.h | 93 + LifeGraphics/render/dx11/RendererDX11.cpp | 1335 +++ LifeGraphics/render/dx11/RendererDX11.h | 213 + LifeGraphics/render/software/IBlendStateS.h | 0 .../render/software/IConstantBufferS.h | 0 .../render/software/IDepthStencilStateS.h | 0 LifeGraphics/render/software/IIndexBufferS.h | 0 LifeGraphics/render/software/IInputLayoutS.h | 0 LifeGraphics/render/software/IPixelShaderS.h | 0 .../render/software/IRasterizerStateS.h | 0 .../render/software/IRenderTargetViewS.h | 0 .../render/software/IShaderResourceViewS.h | 0 LifeGraphics/render/software/ITexture2DS.h | 0 .../render/software/ITextureSamplerS.h | 0 LifeGraphics/render/software/IVertexBufferS.h | 0 LifeGraphics/render/software/IVertexShaderS.h | 0 .../render/software/RendererSoftware.cpp | 14 + LifeGraphics/renderpath/IRenderPath.h | 21 + LifeGraphics/renderpath/IRenderPathShadow.h | 28 + LifeGraphics/renderpath/RenderPathDefault.cpp | 4 + LifeGraphics/renderpath/RenderPathDefault.h | 16 + LifeGraphics/renderpath/RenderPathDeferred.h | 4 + LifeGraphics/renderpath/RenderPathForward.h | 4 + .../renderpath/RenderPathOmniShadow.cpp | 128 + .../renderpath/RenderPathOmniShadow.h | 27 + .../renderpath/RenderPathSpotShadow.cpp | 102 + .../renderpath/RenderPathSpotShadow.h | 27 + LifeGraphics/resource_ptr.h | 57 + LifeGraphics/scene/IScene.cpp | 0 LifeGraphics/scene/ISceneManager.cpp | 0 LifeGraphics/shaders/IShaderFactorySource.cpp | 307 + LifeGraphics/shaders/IShaderFactorySource.h | 45 + LifeGraphics/shaders/IShaderGraph.cpp | 207 + LifeGraphics/shaders/IShaderGraph.h | 40 + LifeGraphics/shaders/IShaderGraphCompiler.h | 70 + LifeGraphics/shaders/IShaderGraphNode.cpp | 59 + LifeGraphics/shaders/IShaderGraphNode.h | 36 + .../shaders/IShaderGraphNodeFactory.cpp | 19 + .../shaders/IShaderGraphNodeFactory.h | 25 + LifeGraphics/shaders/IShaderGraphNodePort.cpp | 58 + LifeGraphics/shaders/IShaderGraphNodePort.h | 30 + LifeGraphics/shaders/IShaderTypes.h | 22 + .../shaders/ShaderGraphCompilerHLSL.cpp | 520 + .../shaders/ShaderGraphCompilerHLSL.h | 24 + LifeGraphics/targetver.h | 24 + LifeGraphics/vegetation/PlantManager.h | 607 ++ LifeMaterialEditor/Editor.cpp | 597 + LifeMaterialEditor/Editor.h | 88 + LifeMaterialEditor/EditorShared.h | 13 + LifeMaterialEditor/GUINodeContainer.cpp | 531 + LifeMaterialEditor/GUINodeContainer.h | 95 + LifeMaterialEditor/GUINodeList.cpp | 0 LifeMaterialEditor/GUINodeList.h | 11 + LifeMaterialEditor/GUINodePort.cpp | 52 + LifeMaterialEditor/GUINodePort.h | 29 + LifeMaterialEditor/GUIShaderNode.cpp | 318 + LifeMaterialEditor/GUIShaderNode.h | 96 + LifeMaterialEditor/GUIShaderNodeIO.cpp | 98 + LifeMaterialEditor/GUIShaderNodeIO.h | 41 + LifeMaterialEditor/GUIShaderNodeTexture.h | 44 + LifeMaterialEditor/LifeMaterialEditor.cpp | 9 + LifeMaterialEditor/LifeMaterialEditor.vcxproj | 191 + LifeMaterialEditor/engine_log.txt | 9479 ++++++++++++++++ LifeMaterialEditor/stdafx.cpp | 8 + LifeMaterialEditor/stdafx.h | 13 + LifeMaterialEditor/targetver.h | 8 + LifeProject.sln | 166 + LifeSound/LifeSound.vcxproj | 189 + LifeSound/src/ISound.h | 33 + LifeSound/src/ISoundStream.cpp | 5 + LifeSound/src/ISoundStream.h | 14 + LifeSound/src/OggStream.cpp | 158 + LifeSound/src/OggStream.h | 45 + LifeSound/src/SoundInfo.h | 16 + LifeSound/src/SoundSystem.cpp | 154 + LifeSound/src/SoundSystem.h | 27 + LifeSound/src/StaticSound.cpp | 55 + LifeSound/src/StaticSound.h | 30 + LifeSound/src/StreamedSound.cpp | 137 + LifeSound/src/StreamedSound.h | 41 + LifeWorldEditor/EditorCameraInputController.h | 13 + LifeWorldEditor/LifeWorldEditor.cpp | 151 + LifeWorldEditor/LifeWorldEditor.h | 22 + LifeWorldEditor/LifeWorldEditor.vcxproj | 173 + LifeWorldEditor/main.cpp | 7 + MyGame/MyGame.vcxproj | 171 + MyGame/include/MyGame.h | 382 + MyGame/include/states/StateGame.h | 27 + MyGame/include/states/StateSettings.h | 33 + MyGame/src/states/StateGame.cpp | 156 + MyGame/src/states/StateSettings.cpp | 70 + README.md | 3 +- TestBed/TestBed.cpp | 96 + TestBed/TestBed.vcxproj | 162 + 610 files changed, 92343 insertions(+), 2 deletions(-) create mode 100644 LifeAudioPlayer/Data.pak create mode 100644 LifeAudioPlayer/LifeAudioPlayer.cpp create mode 100644 LifeAudioPlayer/LifeAudioPlayer.vcxproj create mode 100644 LifeAudioPlayer/stdafx.cpp create mode 100644 LifeAudioPlayer/stdafx.h create mode 100644 LifeAudioPlayer/targetver.h create mode 100644 LifeCore/Array.cpp create mode 100644 LifeCore/Array.h create mode 100644 LifeCore/Axis.h create mode 100644 LifeCore/BoundingBox3D.h create mode 100644 LifeCore/Build.h create mode 100644 LifeCore/Com.h create mode 100644 LifeCore/Delegate.h create mode 100644 LifeCore/DisplayMode.h create mode 100644 LifeCore/Error.h create mode 100644 LifeCore/FrameCounter.cpp create mode 100644 LifeCore/FrameCounter.h create mode 100644 LifeCore/ICamera.h create mode 100644 LifeCore/IDataRectangle.h create mode 100644 LifeCore/IDataStream.h create mode 100644 LifeCore/IDevice.h create mode 100644 LifeCore/IImage.h create mode 100644 LifeCore/IInputSystem.cpp create mode 100644 LifeCore/IInputSystem.h create mode 100644 LifeCore/IMemoryStream.h create mode 100644 LifeCore/IProperty.cpp create mode 100644 LifeCore/IProperty.h create mode 100644 LifeCore/IPropertyHolder.cpp create mode 100644 LifeCore/IPropertyHolder.h create mode 100644 LifeCore/IPropertyItem.h create mode 100644 LifeCore/ISerializable.h create mode 100644 LifeCore/ITransform.h create mode 100644 LifeCore/IWindow.h create mode 100644 LifeCore/IWindowDemander.h create mode 100644 LifeCore/InputDevice.h create mode 100644 LifeCore/InputListener222.h create mode 100644 LifeCore/InputSystem.cpp create mode 100644 LifeCore/InputSystem.h create mode 100644 LifeCore/InputSystem222.h create mode 100644 LifeCore/InputSystemOIS22.cpp create mode 100644 LifeCore/InputSystemOIS22.h create mode 100644 LifeCore/InputTypes222.h create mode 100644 LifeCore/LString.cpp create mode 100644 LifeCore/LString.h create mode 100644 LifeCore/LifeCore.vcxproj create mode 100644 LifeCore/PixelBuffer.cpp create mode 100644 LifeCore/PixelBuffer.h create mode 100644 LifeCore/Property.h create mode 100644 LifeCore/Range.h create mode 100644 LifeCore/RawBuffer.h create mode 100644 LifeCore/Singleton.h create mode 100644 LifeCore/Timer.cpp create mode 100644 LifeCore/Timer.h create mode 100644 LifeCore/Types.h create mode 100644 LifeCore/Utils.cpp create mode 100644 LifeCore/Utils.h create mode 100644 LifeCore/Window.h create mode 100644 LifeCore/WindowPixelManager.cpp create mode 100644 LifeCore/WindowPixelManager.h create mode 100644 LifeCore/WindowWin.cpp create mode 100644 LifeCore/WindowWin.h create mode 100644 LifeCore/delegate/Delegate.h create mode 100644 LifeCore/delegate/Delegate_base.h create mode 100644 LifeCore/log/CLog.cpp create mode 100644 LifeCore/log/CLog.h create mode 100644 LifeCore/log/NLog.cpp create mode 100644 LifeCore/log/NLog.h create mode 100644 LifeCore/math/LifeMath.cpp create mode 100644 LifeCore/math/LifeMath.h create mode 100644 LifeCore/math/Matrix4x4.h create mode 100644 LifeCore/math/Plane.h create mode 100644 LifeCore/math/Quaternion.h create mode 100644 LifeCore/math/Rectangle.h create mode 100644 LifeCore/math/Spline.h create mode 100644 LifeCore/math/Vector2D.h create mode 100644 LifeCore/math/Vector3D.h create mode 100644 LifeCore/math/Vector4D.h create mode 100644 LifeCore/math/vector_base.h create mode 100644 LifeCore/media/WebCamera.cpp create mode 100644 LifeCore/media/WebCamera.h create mode 100644 LifeCore/media/WinMfFormatReader.cpp create mode 100644 LifeCore/media/WinMfFormatReader.h create mode 100644 LifeCore/media/WinMfImageGrabber.cpp create mode 100644 LifeCore/media/WinMfImageGrabber.h create mode 100644 LifeCore/media/WinMfMediaType.h create mode 100644 LifeCore/minizip/crypt.h create mode 100644 LifeCore/minizip/ioapi.c create mode 100644 LifeCore/minizip/ioapi.h create mode 100644 LifeCore/minizip/iowin32.c create mode 100644 LifeCore/minizip/iowin32.h create mode 100644 LifeCore/minizip/miniunz.c create mode 100644 LifeCore/minizip/minizip.c create mode 100644 LifeCore/minizip/mztools.c create mode 100644 LifeCore/minizip/mztools.h create mode 100644 LifeCore/minizip/unzip.c create mode 100644 LifeCore/minizip/unzip.h create mode 100644 LifeCore/minizip/zip.c create mode 100644 LifeCore/minizip/zip.h create mode 100644 LifeCore/tools/CompilerDetect.h create mode 100644 LifeCore/tools/Inline.h create mode 100644 LifeCore/tools/OsDetect.h create mode 100644 LifeCore/vfs/IContentProvider.h create mode 100644 LifeCore/vfs/IFile.h create mode 100644 LifeCore/vfs/IFileSystem.cpp create mode 100644 LifeCore/vfs/IFileSystem.h create mode 100644 LifeCore/vfs/IFolder.h create mode 100644 LifeCore/vfs/RootFolder.h create mode 100644 LifeCore/vfs/WinFileStream.cpp create mode 100644 LifeCore/vfs/WinFileStream.h create mode 100644 LifeCore/vfs/fs_win/IFSContentProvider.cpp create mode 100644 LifeCore/vfs/fs_win/IFSContentProvider.h create mode 100644 LifeCore/vfs/fs_win/IFSFile.cpp create mode 100644 LifeCore/vfs/fs_win/IFSFile.h create mode 100644 LifeCore/vfs/fs_win/IFSFolder.cpp create mode 100644 LifeCore/vfs/fs_win/IFSFolder.h create mode 100644 LifeCore/vfs/fs_win/IFSStream.h create mode 100644 LifeCore/vfs/pak/IPakFile.cpp create mode 100644 LifeCore/vfs/pak/IPakFile.h create mode 100644 LifeCore/vfs/pak/IPakFileContentProvider.cpp create mode 100644 LifeCore/vfs/pak/IPakFileContentProvider.h create mode 100644 LifeCore/vfs/pak/IPakFolder.cpp create mode 100644 LifeCore/vfs/pak/IPakFolder.h create mode 100644 LifeCore/vfs/pak/IPakStream.h create mode 100644 LifeCore/vfs/pak/pak_io/EntryInfo.h create mode 100644 LifeCore/vfs/pak/pak_io/FileEntry.h create mode 100644 LifeCore/vfs/pak/pak_io/FolderEntry.h create mode 100644 LifeCore/vfs/pak/pak_io/PakFile.cpp create mode 100644 LifeCore/vfs/pak/pak_io/PakFile.h create mode 100644 LifeCore/vfs/pak/pak_io/PakTypes.h create mode 100644 LifeCore/vfs/pak/pak_io/pakHeader.h create mode 100644 LifeCore/vfs/pak/pak_io/targetver.h create mode 100644 LifeCore/vfs/zip/IZipFile.cpp create mode 100644 LifeCore/vfs/zip/IZipFile.h create mode 100644 LifeCore/vfs/zip/IZipFileContentProvider.cpp create mode 100644 LifeCore/vfs/zip/IZipFileContentProvider.h create mode 100644 LifeCore/vfs/zip/IZipFolder.cpp create mode 100644 LifeCore/vfs/zip/IZipFolder.h create mode 100644 LifeCore/vfs/zip/IZipStream.h create mode 100644 LifeExport/Exports.def create mode 100644 LifeExport/LifeExport.cpp create mode 100644 LifeExport/LifeExport.h create mode 100644 LifeExport/LifeExport.vcxproj create mode 100644 LifeExport/dllmain.cpp create mode 100644 LifeExport/stdafx.cpp create mode 100644 LifeExport/stdafx.h create mode 100644 LifeExport/targetver.h create mode 100644 LifeExportInternal/Exports.def create mode 100644 LifeExportInternal/LifeExportInternal.cpp create mode 100644 LifeExportInternal/LifeExportInternal.h create mode 100644 LifeExportInternal/LifeExportInternal.vcxproj create mode 100644 LifeExportInternal/ReadMe.txt create mode 100644 LifeExportInternal/dllmain.cpp create mode 100644 LifeExportInternal/stdafx.cpp create mode 100644 LifeExportInternal/stdafx.h create mode 100644 LifeExportInternal/targetver.h create mode 100644 LifeGUI/LifeGUI.vcxproj create mode 100644 LifeGUI/src/GUIBorder.h create mode 100644 LifeGUI/src/GUIButton.cpp create mode 100644 LifeGUI/src/GUIButton.h create mode 100644 LifeGUI/src/GUICheckBox.cpp create mode 100644 LifeGUI/src/GUICheckBox.h create mode 100644 LifeGUI/src/GUIComboBox.cpp create mode 100644 LifeGUI/src/GUIComboBox.h create mode 100644 LifeGUI/src/GUIFileBrowser.cpp create mode 100644 LifeGUI/src/GUIFileBrowser.h create mode 100644 LifeGUI/src/GUIFileBrowserDialog.cpp create mode 100644 LifeGUI/src/GUIFileBrowserDialog.h create mode 100644 LifeGUI/src/GUIFont.cpp create mode 100644 LifeGUI/src/GUIFont.h create mode 100644 LifeGUI/src/GUIFontGlyph.h create mode 100644 LifeGUI/src/GUIFontManager.cpp create mode 100644 LifeGUI/src/GUIFontManager.h create mode 100644 LifeGUI/src/GUIForm.cpp create mode 100644 LifeGUI/src/GUIForm.h create mode 100644 LifeGUI/src/GUIList.cpp create mode 100644 LifeGUI/src/GUIList.h create mode 100644 LifeGUI/src/GUIManager.cpp create mode 100644 LifeGUI/src/GUIManager.h create mode 100644 LifeGUI/src/GUIMenu.cpp create mode 100644 LifeGUI/src/GUIMenu.h create mode 100644 LifeGUI/src/GUIMenuItem.cpp create mode 100644 LifeGUI/src/GUIMenuItem.h create mode 100644 LifeGUI/src/GUIMenuTextItem.h create mode 100644 LifeGUI/src/GUIMessaging.h create mode 100644 LifeGUI/src/GUIOverlay.cpp create mode 100644 LifeGUI/src/GUIOverlay.h create mode 100644 LifeGUI/src/GUIOverlayManager.cpp create mode 100644 LifeGUI/src/GUIOverlayManager.h create mode 100644 LifeGUI/src/GUIPicture.cpp create mode 100644 LifeGUI/src/GUIPicture.h create mode 100644 LifeGUI/src/GUIPlane.cpp create mode 100644 LifeGUI/src/GUIPlane.h create mode 100644 LifeGUI/src/GUIProgress.cpp create mode 100644 LifeGUI/src/GUIProgress.h create mode 100644 LifeGUI/src/GUIPropertyGrid.cpp create mode 100644 LifeGUI/src/GUIPropertyGrid.h create mode 100644 LifeGUI/src/GUIRadioButton.cpp create mode 100644 LifeGUI/src/GUIRadioButton.h create mode 100644 LifeGUI/src/GUIRenderWindow.cpp create mode 100644 LifeGUI/src/GUIRenderWindow.h create mode 100644 LifeGUI/src/GUIScroll.cpp create mode 100644 LifeGUI/src/GUIScroll.h create mode 100644 LifeGUI/src/GUIScrollablePlane.cpp create mode 100644 LifeGUI/src/GUIScrollablePlane.h create mode 100644 LifeGUI/src/GUISlider.cpp create mode 100644 LifeGUI/src/GUISlider.h create mode 100644 LifeGUI/src/GUISplitPanel.cpp create mode 100644 LifeGUI/src/GUISplitPanel.h create mode 100644 LifeGUI/src/GUITab.cpp create mode 100644 LifeGUI/src/GUITab.h create mode 100644 LifeGUI/src/GUITabPage.h create mode 100644 LifeGUI/src/GUIText.cpp create mode 100644 LifeGUI/src/GUIText.h create mode 100644 LifeGUI/src/GUITextInput.cpp create mode 100644 LifeGUI/src/GUITextInput.h create mode 100644 LifeGUI/src/GUIToolStrip.cpp create mode 100644 LifeGUI/src/GUIToolStrip.h create mode 100644 LifeGUI/src/GUIToolStripButton.cpp create mode 100644 LifeGUI/src/GUIToolStripButton.h create mode 100644 LifeGUI/src/GUIToolStripImage.cpp create mode 100644 LifeGUI/src/GUIToolStripImage.h create mode 100644 LifeGUI/src/GUIToolStripItem.cpp create mode 100644 LifeGUI/src/GUIToolStripItem.h create mode 100644 LifeGUI/src/GUIToolStripMenu.cpp create mode 100644 LifeGUI/src/GUIToolStripMenu.h create mode 100644 LifeGUI/src/GUITreeNode.cpp create mode 100644 LifeGUI/src/GUITreeNode.h create mode 100644 LifeGUI/src/GUITreeView.cpp create mode 100644 LifeGUI/src/GUITreeView.h create mode 100644 LifeGUI/src/GUIVertex.h create mode 100644 LifeGUI/src/GUIWidgetSkin.h create mode 100644 LifeGUI/src/GUIWindow.cpp create mode 100644 LifeGUI/src/GUIWindow.h create mode 100644 LifeGUI/src/IGUICursor.h create mode 100644 LifeGUI/src/IGUIDataManager.h create mode 100644 LifeGUI/src/IGUIRenderer.cpp create mode 100644 LifeGUI/src/IGUIRenderer.h create mode 100644 LifeGUI/src/IImage.h create mode 100644 LifeGUI/src/PropertyItemFile.h create mode 100644 LifeGUI/src/PropertyItemFloat.h create mode 100644 LifeGame/LifeGame.vcxproj create mode 100644 LifeGame/src/Action.cpp create mode 100644 LifeGame/src/Action.h create mode 100644 LifeGame/src/ActionSystem.cpp create mode 100644 LifeGame/src/ActionSystem.h create mode 100644 LifeGame/src/BulletLibs.h create mode 100644 LifeGame/src/CGUIDataManager.cpp create mode 100644 LifeGame/src/CGUIDataManager.h create mode 100644 LifeGame/src/CGUIRenderer.cpp create mode 100644 LifeGame/src/CGUIRenderer.h create mode 100644 LifeGame/src/CImage.cpp create mode 100644 LifeGame/src/CImage.h create mode 100644 LifeGame/src/Entity.cpp create mode 100644 LifeGame/src/Entity.h create mode 100644 LifeGame/src/EntityFactory.h create mode 100644 LifeGame/src/GOCCameraFree.cpp create mode 100644 LifeGame/src/GOCCameraFree.h create mode 100644 LifeGame/src/GOCLogic.h create mode 100644 LifeGame/src/GOComponent.h create mode 100644 LifeGame/src/GUIOutput.h create mode 100644 LifeGame/src/Game.cpp create mode 100644 LifeGame/src/Game.h create mode 100644 LifeGame/src/GameCamera.h create mode 100644 LifeGame/src/GameGlobals.h create mode 100644 LifeGame/src/Graphics.h create mode 100644 LifeGame/src/IActionListener.h create mode 100644 LifeGame/src/IActionProvider.h create mode 100644 LifeGame/src/IGraphicsEntity.cpp create mode 100644 LifeGame/src/IGraphicsEntity.h create mode 100644 LifeGame/src/IPhysicsEntity.cpp create mode 100644 LifeGame/src/IPhysicsEntity.h create mode 100644 LifeGame/src/IPhysicsWorld.h create mode 100644 LifeGame/src/IScriptEntity.h create mode 100644 LifeGame/src/InputActionProvider.cpp create mode 100644 LifeGame/src/InputActionProvider.h create mode 100644 LifeGame/src/World.cpp create mode 100644 LifeGame/src/World.h create mode 100644 LifeGame/src/script/Script.cpp create mode 100644 LifeGame/src/script/Script.h create mode 100644 LifeGame/src/script/ScriptSystem.cpp create mode 100644 LifeGame/src/script/ScriptSystem.h create mode 100644 LifeGraphics/AnimationController.h create mode 100644 LifeGraphics/AnimationFrame.h create mode 100644 LifeGraphics/AnimationTrack.h create mode 100644 LifeGraphics/Bone.h create mode 100644 LifeGraphics/BoneTransform.h create mode 100644 LifeGraphics/BoundingSphere.h create mode 100644 LifeGraphics/CG.cpp create mode 100644 LifeGraphics/CG.h create mode 100644 LifeGraphics/Camera.cpp create mode 100644 LifeGraphics/CustomSwapChain.cpp create mode 100644 LifeGraphics/CustomSwapChain.h create mode 100644 LifeGraphics/DayTime.h create mode 100644 LifeGraphics/GeometrySubset.h create mode 100644 LifeGraphics/Globals.h create mode 100644 LifeGraphics/IGraphicsNode.h create mode 100644 LifeGraphics/IMesh.h create mode 100644 LifeGraphics/IObject.h create mode 100644 LifeGraphics/IObjectMesh.h create mode 100644 LifeGraphics/IRenderOutput.cpp create mode 100644 LifeGraphics/IRenderOutput.h create mode 100644 LifeGraphics/IResource.h create mode 100644 LifeGraphics/IResourceCollection.h create mode 100644 LifeGraphics/IScene.h create mode 100644 LifeGraphics/ISkinnedMesh.h create mode 100644 LifeGraphics/ISwapChain.h create mode 100644 LifeGraphics/LifeDebug.h create mode 100644 LifeGraphics/LifeEngine.config create mode 100644 LifeGraphics/LifeEngine.cpp create mode 100644 LifeGraphics/LifeEngine.h create mode 100644 LifeGraphics/LifeGraphics.vcxproj create mode 100644 LifeGraphics/Light.h create mode 100644 LifeGraphics/MeshManager.cpp create mode 100644 LifeGraphics/MeshManager.h create mode 100644 LifeGraphics/PassGBuffer.h create mode 100644 LifeGraphics/Plugin.cpp create mode 100644 LifeGraphics/Plugin.h create mode 100644 LifeGraphics/PluginServer.cpp create mode 100644 LifeGraphics/PluginServer.h create mode 100644 LifeGraphics/Rectangle2D.h create mode 100644 LifeGraphics/RenderOperation.h create mode 100644 LifeGraphics/RenderPass.h create mode 100644 LifeGraphics/RenderQueue.h create mode 100644 LifeGraphics/ResourceManager.cpp create mode 100644 LifeGraphics/ResourceManager.h create mode 100644 LifeGraphics/ShadowMapManager.h create mode 100644 LifeGraphics/Skeleton.h create mode 100644 LifeGraphics/SkinInfo.h create mode 100644 LifeGraphics/Sky.h create mode 100644 LifeGraphics/Terrain.h create mode 100644 LifeGraphics/TypesVideo.h create mode 100644 LifeGraphics/Vertex.h create mode 100644 LifeGraphics/XML.h create mode 100644 LifeGraphics/camera/Camera.h create mode 100644 LifeGraphics/camera/Frustum.h create mode 100644 LifeGraphics/constants.h create mode 100644 LifeGraphics/debug/IDebugDrawer.h create mode 100644 LifeGraphics/passPSSM.h create mode 100644 LifeGraphics/pugiconfig.hpp create mode 100644 LifeGraphics/pugixml.cpp create mode 100644 LifeGraphics/pugixml.hpp create mode 100644 LifeGraphics/pugixml/contrib/foreach.hpp create mode 100644 LifeGraphics/pugixml/docs/images/caution.png create mode 100644 LifeGraphics/pugixml/docs/images/dom_tree.png create mode 100644 LifeGraphics/pugixml/docs/images/dom_tree_thumb.png create mode 100644 LifeGraphics/pugixml/docs/images/home.png create mode 100644 LifeGraphics/pugixml/docs/images/next.png create mode 100644 LifeGraphics/pugixml/docs/images/note.png create mode 100644 LifeGraphics/pugixml/docs/images/prev.png create mode 100644 LifeGraphics/pugixml/docs/images/up.png create mode 100644 LifeGraphics/pugixml/docs/images/vs2005_link1.png create mode 100644 LifeGraphics/pugixml/docs/images/vs2005_link1_thumb.png create mode 100644 LifeGraphics/pugixml/docs/images/vs2005_link2.png create mode 100644 LifeGraphics/pugixml/docs/images/vs2005_link2_thumb.png create mode 100644 LifeGraphics/pugixml/docs/images/vs2005_pch1.png create mode 100644 LifeGraphics/pugixml/docs/images/vs2005_pch1_thumb.png create mode 100644 LifeGraphics/pugixml/docs/images/vs2005_pch2.png create mode 100644 LifeGraphics/pugixml/docs/images/vs2005_pch2_thumb.png create mode 100644 LifeGraphics/pugixml/docs/images/vs2005_pch3.png create mode 100644 LifeGraphics/pugixml/docs/images/vs2005_pch3_thumb.png create mode 100644 LifeGraphics/pugixml/docs/images/vs2005_pch4.png create mode 100644 LifeGraphics/pugixml/docs/images/vs2005_pch4_thumb.png create mode 100644 LifeGraphics/pugixml/docs/images/vs2010_link1.png create mode 100644 LifeGraphics/pugixml/docs/images/vs2010_link1_thumb.png create mode 100644 LifeGraphics/pugixml/docs/images/vs2010_link2.png create mode 100644 LifeGraphics/pugixml/docs/images/vs2010_link2_thumb.png create mode 100644 LifeGraphics/pugixml/docs/manual.html create mode 100644 LifeGraphics/pugixml/docs/manual/access.html create mode 100644 LifeGraphics/pugixml/docs/manual/apiref.html create mode 100644 LifeGraphics/pugixml/docs/manual/changes.html create mode 100644 LifeGraphics/pugixml/docs/manual/dom.html create mode 100644 LifeGraphics/pugixml/docs/manual/install.html create mode 100644 LifeGraphics/pugixml/docs/manual/loading.html create mode 100644 LifeGraphics/pugixml/docs/manual/modify.html create mode 100644 LifeGraphics/pugixml/docs/manual/saving.html create mode 100644 LifeGraphics/pugixml/docs/manual/toc.html create mode 100644 LifeGraphics/pugixml/docs/manual/xpath.html create mode 100644 LifeGraphics/pugixml/docs/pugixml.css create mode 100644 LifeGraphics/pugixml/docs/quickstart.html create mode 100644 LifeGraphics/pugixml/docs/samples/character.xml create mode 100644 LifeGraphics/pugixml/docs/samples/custom_memory_management.cpp create mode 100644 LifeGraphics/pugixml/docs/samples/include.cpp create mode 100644 LifeGraphics/pugixml/docs/samples/load_error_handling.cpp create mode 100644 LifeGraphics/pugixml/docs/samples/load_file.cpp create mode 100644 LifeGraphics/pugixml/docs/samples/load_memory.cpp create mode 100644 LifeGraphics/pugixml/docs/samples/load_options.cpp create mode 100644 LifeGraphics/pugixml/docs/samples/load_stream.cpp create mode 100644 LifeGraphics/pugixml/docs/samples/modify_add.cpp create mode 100644 LifeGraphics/pugixml/docs/samples/modify_base.cpp create mode 100644 LifeGraphics/pugixml/docs/samples/modify_remove.cpp create mode 100644 LifeGraphics/pugixml/docs/samples/save_custom_writer.cpp create mode 100644 LifeGraphics/pugixml/docs/samples/save_file.cpp create mode 100644 LifeGraphics/pugixml/docs/samples/save_file_output.xml create mode 100644 LifeGraphics/pugixml/docs/samples/save_options.cpp create mode 100644 LifeGraphics/pugixml/docs/samples/save_stream.cpp create mode 100644 LifeGraphics/pugixml/docs/samples/save_subtree.cpp create mode 100644 LifeGraphics/pugixml/docs/samples/transitions.xml create mode 100644 LifeGraphics/pugixml/docs/samples/traverse_base.cpp create mode 100644 LifeGraphics/pugixml/docs/samples/traverse_iter.cpp create mode 100644 LifeGraphics/pugixml/docs/samples/traverse_predicate.cpp create mode 100644 LifeGraphics/pugixml/docs/samples/traverse_walker.cpp create mode 100644 LifeGraphics/pugixml/docs/samples/tree.xml create mode 100644 LifeGraphics/pugixml/docs/samples/weekly-shift_jis.xml create mode 100644 LifeGraphics/pugixml/docs/samples/weekly-utf-16.xml create mode 100644 LifeGraphics/pugixml/docs/samples/weekly-utf-8.xml create mode 100644 LifeGraphics/pugixml/docs/samples/xgconsole.xml create mode 100644 LifeGraphics/pugixml/docs/samples/xpath_error.cpp create mode 100644 LifeGraphics/pugixml/docs/samples/xpath_query.cpp create mode 100644 LifeGraphics/pugixml/docs/samples/xpath_select.cpp create mode 100644 LifeGraphics/pugixml/docs/samples/xpath_variables.cpp create mode 100644 LifeGraphics/pugixml/lib/pugixml_s.lib create mode 100644 LifeGraphics/pugixml/lib/pugixml_s.pdb create mode 100644 LifeGraphics/pugixml/lib/pugixml_sd.lib create mode 100644 LifeGraphics/pugixml/lib/pugixml_sd.pdb create mode 100644 LifeGraphics/pugixml/readme.txt create mode 100644 LifeGraphics/pugixml/scripts/CMakeLists.txt create mode 100644 LifeGraphics/pugixml/scripts/premake4.lua create mode 100644 LifeGraphics/pugixml/scripts/pugixml.xcodeproj/project.pbxproj create mode 100644 LifeGraphics/pugixml/scripts/pugixml_codeblocks.cbp create mode 100644 LifeGraphics/pugixml/scripts/pugixml_codelite.project create mode 100644 LifeGraphics/pugixml/scripts/pugixml_vs2005.vcproj create mode 100644 LifeGraphics/pugixml/scripts/pugixml_vs2005_static.vcproj create mode 100644 LifeGraphics/pugixml/scripts/pugixml_vs2008.vcproj create mode 100644 LifeGraphics/pugixml/scripts/pugixml_vs2008_static.vcproj create mode 100644 LifeGraphics/pugixml/scripts/pugixml_vs2010.vcxproj create mode 100644 LifeGraphics/pugixml/scripts/pugixml_vs2010_static.sln create mode 100644 LifeGraphics/pugixml/scripts/pugixml_vs2010_static.suo create mode 100644 LifeGraphics/pugixml/scripts/pugixml_vs2010_static.vcxproj create mode 100644 LifeGraphics/pugixml/scripts/pugixml_vs2010_static.vcxproj.user create mode 100644 LifeGraphics/pugixml/scripts/vs2010/x32/Debug/CL.read.1.tlog create mode 100644 LifeGraphics/pugixml/scripts/vs2010/x32/Debug/CL.write.1.tlog create mode 100644 LifeGraphics/pugixml/scripts/vs2010/x32/Debug/Lib-link.read.1.tlog create mode 100644 LifeGraphics/pugixml/scripts/vs2010/x32/Debug/Lib-link.write.1.tlog create mode 100644 LifeGraphics/pugixml/scripts/vs2010/x32/Debug/Lib.read.1.tlog create mode 100644 LifeGraphics/pugixml/scripts/vs2010/x32/Debug/cl.command.1.tlog create mode 100644 LifeGraphics/pugixml/scripts/vs2010/x32/Debug/lib.command.1.tlog create mode 100644 LifeGraphics/pugixml/scripts/vs2010/x32/Debug/pugixml.lastbuildstate create mode 100644 LifeGraphics/pugixml/scripts/vs2010/x32/Debug/pugixml.obj create mode 100644 LifeGraphics/pugixml/scripts/vs2010/x32/Debug/pugixml_vs2010_static.log create mode 100644 LifeGraphics/pugixml/scripts/vs2010/x32/Release/CL.read.1.tlog create mode 100644 LifeGraphics/pugixml/scripts/vs2010/x32/Release/CL.write.1.tlog create mode 100644 LifeGraphics/pugixml/scripts/vs2010/x32/Release/Lib-link.read.1.tlog create mode 100644 LifeGraphics/pugixml/scripts/vs2010/x32/Release/Lib-link.write.1.tlog create mode 100644 LifeGraphics/pugixml/scripts/vs2010/x32/Release/Lib.read.1.tlog create mode 100644 LifeGraphics/pugixml/scripts/vs2010/x32/Release/cl.command.1.tlog create mode 100644 LifeGraphics/pugixml/scripts/vs2010/x32/Release/lib.command.1.tlog create mode 100644 LifeGraphics/pugixml/scripts/vs2010/x32/Release/pugixml.lastbuildstate create mode 100644 LifeGraphics/pugixml/scripts/vs2010/x32/Release/pugixml.obj create mode 100644 LifeGraphics/pugixml/scripts/vs2010/x32/Release/pugixml_vs2010_static.log create mode 100644 LifeGraphics/pugixml/scripts/vs2010/x32/pugixml_s.lib create mode 100644 LifeGraphics/pugixml/scripts/vs2010/x32/pugixml_s.pdb create mode 100644 LifeGraphics/pugixml/scripts/vs2010/x32/pugixml_sd.lib create mode 100644 LifeGraphics/pugixml/scripts/vs2010/x32/pugixml_sd.pdb create mode 100644 LifeGraphics/pugixml/src/pugiconfig.hpp create mode 100644 LifeGraphics/pugixml/src/pugixml.cpp create mode 100644 LifeGraphics/pugixml/src/pugixml.hpp create mode 100644 LifeGraphics/queries/IFrustumQuery.cpp create mode 100644 LifeGraphics/queries/IFrustumQuery.h create mode 100644 LifeGraphics/queries/INodeQuery.h create mode 100644 LifeGraphics/queries/IQuery.h create mode 100644 LifeGraphics/render/Enums.h create mode 100644 LifeGraphics/render/IBlendState.h create mode 100644 LifeGraphics/render/IConstantBuffer.h create mode 100644 LifeGraphics/render/IConstantMapping.cpp create mode 100644 LifeGraphics/render/IConstantMapping.h create mode 100644 LifeGraphics/render/IDataBuffer.h create mode 100644 LifeGraphics/render/IDepthStencilState.h create mode 100644 LifeGraphics/render/IIndexBuffer.h create mode 100644 LifeGraphics/render/IInputElement.h create mode 100644 LifeGraphics/render/IInputLayout.h create mode 100644 LifeGraphics/render/IMacroDefine.h create mode 100644 LifeGraphics/render/IMaterial.cpp create mode 100644 LifeGraphics/render/IMaterial.h create mode 100644 LifeGraphics/render/IMaterialProxy.cpp create mode 100644 LifeGraphics/render/IMaterialProxy.h create mode 100644 LifeGraphics/render/IRasterizerState.h create mode 100644 LifeGraphics/render/IRenderTargetView.h create mode 100644 LifeGraphics/render/IRenderer.cpp create mode 100644 LifeGraphics/render/IRenderer.h create mode 100644 LifeGraphics/render/IShader.h create mode 100644 LifeGraphics/render/IShaderResourceView.h create mode 100644 LifeGraphics/render/ISurface.h create mode 100644 LifeGraphics/render/ITexture2D.h create mode 100644 LifeGraphics/render/ITextureContainer.h create mode 100644 LifeGraphics/render/ITextureCube.h create mode 100644 LifeGraphics/render/ITextureDescription.h create mode 100644 LifeGraphics/render/ITextureSampler.h create mode 100644 LifeGraphics/render/IVertexBuffer.h create mode 100644 LifeGraphics/render/IVideoTexture.h create mode 100644 LifeGraphics/render/PostDOF.h create mode 100644 LifeGraphics/render/PostEffect.h create mode 100644 LifeGraphics/render/PostHDR.h create mode 100644 LifeGraphics/render/PostSSAO.cpp create mode 100644 LifeGraphics/render/PostSSAO.h create mode 100644 LifeGraphics/render/PosteffectManager.h create mode 100644 LifeGraphics/render/RenderParams.h create mode 100644 LifeGraphics/render/RendererDX10.cpp create mode 100644 LifeGraphics/render/RendererDX10.h create mode 100644 LifeGraphics/render/RendererSoftware.h create mode 100644 LifeGraphics/render/dx11/IBlendState11.h create mode 100644 LifeGraphics/render/dx11/IConstantBuffer11.cpp create mode 100644 LifeGraphics/render/dx11/IConstantBuffer11.h create mode 100644 LifeGraphics/render/dx11/IDepthStencilState11.h create mode 100644 LifeGraphics/render/dx11/IIndexBuffer11.h create mode 100644 LifeGraphics/render/dx11/IInputLayout11.h create mode 100644 LifeGraphics/render/dx11/IPixelShader11.h create mode 100644 LifeGraphics/render/dx11/IRasterizerState11.h create mode 100644 LifeGraphics/render/dx11/IRenderTargetViewDX11.h create mode 100644 LifeGraphics/render/dx11/IShaderResourceView11.h create mode 100644 LifeGraphics/render/dx11/ISurface11.cpp create mode 100644 LifeGraphics/render/dx11/ISurface11.h create mode 100644 LifeGraphics/render/dx11/ISwapChain11.cpp create mode 100644 LifeGraphics/render/dx11/ISwapChain11.h create mode 100644 LifeGraphics/render/dx11/ITexture2D11.h create mode 100644 LifeGraphics/render/dx11/ITextureSampler11.h create mode 100644 LifeGraphics/render/dx11/IVertexBuffer11.h create mode 100644 LifeGraphics/render/dx11/IVertexShader11.h create mode 100644 LifeGraphics/render/dx11/RendererDX11.cpp create mode 100644 LifeGraphics/render/dx11/RendererDX11.h create mode 100644 LifeGraphics/render/software/IBlendStateS.h create mode 100644 LifeGraphics/render/software/IConstantBufferS.h create mode 100644 LifeGraphics/render/software/IDepthStencilStateS.h create mode 100644 LifeGraphics/render/software/IIndexBufferS.h create mode 100644 LifeGraphics/render/software/IInputLayoutS.h create mode 100644 LifeGraphics/render/software/IPixelShaderS.h create mode 100644 LifeGraphics/render/software/IRasterizerStateS.h create mode 100644 LifeGraphics/render/software/IRenderTargetViewS.h create mode 100644 LifeGraphics/render/software/IShaderResourceViewS.h create mode 100644 LifeGraphics/render/software/ITexture2DS.h create mode 100644 LifeGraphics/render/software/ITextureSamplerS.h create mode 100644 LifeGraphics/render/software/IVertexBufferS.h create mode 100644 LifeGraphics/render/software/IVertexShaderS.h create mode 100644 LifeGraphics/render/software/RendererSoftware.cpp create mode 100644 LifeGraphics/renderpath/IRenderPath.h create mode 100644 LifeGraphics/renderpath/IRenderPathShadow.h create mode 100644 LifeGraphics/renderpath/RenderPathDefault.cpp create mode 100644 LifeGraphics/renderpath/RenderPathDefault.h create mode 100644 LifeGraphics/renderpath/RenderPathDeferred.h create mode 100644 LifeGraphics/renderpath/RenderPathForward.h create mode 100644 LifeGraphics/renderpath/RenderPathOmniShadow.cpp create mode 100644 LifeGraphics/renderpath/RenderPathOmniShadow.h create mode 100644 LifeGraphics/renderpath/RenderPathSpotShadow.cpp create mode 100644 LifeGraphics/renderpath/RenderPathSpotShadow.h create mode 100644 LifeGraphics/resource_ptr.h create mode 100644 LifeGraphics/scene/IScene.cpp create mode 100644 LifeGraphics/scene/ISceneManager.cpp create mode 100644 LifeGraphics/shaders/IShaderFactorySource.cpp create mode 100644 LifeGraphics/shaders/IShaderFactorySource.h create mode 100644 LifeGraphics/shaders/IShaderGraph.cpp create mode 100644 LifeGraphics/shaders/IShaderGraph.h create mode 100644 LifeGraphics/shaders/IShaderGraphCompiler.h create mode 100644 LifeGraphics/shaders/IShaderGraphNode.cpp create mode 100644 LifeGraphics/shaders/IShaderGraphNode.h create mode 100644 LifeGraphics/shaders/IShaderGraphNodeFactory.cpp create mode 100644 LifeGraphics/shaders/IShaderGraphNodeFactory.h create mode 100644 LifeGraphics/shaders/IShaderGraphNodePort.cpp create mode 100644 LifeGraphics/shaders/IShaderGraphNodePort.h create mode 100644 LifeGraphics/shaders/IShaderTypes.h create mode 100644 LifeGraphics/shaders/ShaderGraphCompilerHLSL.cpp create mode 100644 LifeGraphics/shaders/ShaderGraphCompilerHLSL.h create mode 100644 LifeGraphics/targetver.h create mode 100644 LifeGraphics/vegetation/PlantManager.h create mode 100644 LifeMaterialEditor/Editor.cpp create mode 100644 LifeMaterialEditor/Editor.h create mode 100644 LifeMaterialEditor/EditorShared.h create mode 100644 LifeMaterialEditor/GUINodeContainer.cpp create mode 100644 LifeMaterialEditor/GUINodeContainer.h create mode 100644 LifeMaterialEditor/GUINodeList.cpp create mode 100644 LifeMaterialEditor/GUINodeList.h create mode 100644 LifeMaterialEditor/GUINodePort.cpp create mode 100644 LifeMaterialEditor/GUINodePort.h create mode 100644 LifeMaterialEditor/GUIShaderNode.cpp create mode 100644 LifeMaterialEditor/GUIShaderNode.h create mode 100644 LifeMaterialEditor/GUIShaderNodeIO.cpp create mode 100644 LifeMaterialEditor/GUIShaderNodeIO.h create mode 100644 LifeMaterialEditor/GUIShaderNodeTexture.h create mode 100644 LifeMaterialEditor/LifeMaterialEditor.cpp create mode 100644 LifeMaterialEditor/LifeMaterialEditor.vcxproj create mode 100644 LifeMaterialEditor/engine_log.txt create mode 100644 LifeMaterialEditor/stdafx.cpp create mode 100644 LifeMaterialEditor/stdafx.h create mode 100644 LifeMaterialEditor/targetver.h create mode 100644 LifeProject.sln create mode 100644 LifeSound/LifeSound.vcxproj create mode 100644 LifeSound/src/ISound.h create mode 100644 LifeSound/src/ISoundStream.cpp create mode 100644 LifeSound/src/ISoundStream.h create mode 100644 LifeSound/src/OggStream.cpp create mode 100644 LifeSound/src/OggStream.h create mode 100644 LifeSound/src/SoundInfo.h create mode 100644 LifeSound/src/SoundSystem.cpp create mode 100644 LifeSound/src/SoundSystem.h create mode 100644 LifeSound/src/StaticSound.cpp create mode 100644 LifeSound/src/StaticSound.h create mode 100644 LifeSound/src/StreamedSound.cpp create mode 100644 LifeSound/src/StreamedSound.h create mode 100644 LifeWorldEditor/EditorCameraInputController.h create mode 100644 LifeWorldEditor/LifeWorldEditor.cpp create mode 100644 LifeWorldEditor/LifeWorldEditor.h create mode 100644 LifeWorldEditor/LifeWorldEditor.vcxproj create mode 100644 LifeWorldEditor/main.cpp create mode 100644 MyGame/MyGame.vcxproj create mode 100644 MyGame/include/MyGame.h create mode 100644 MyGame/include/states/StateGame.h create mode 100644 MyGame/include/states/StateSettings.h create mode 100644 MyGame/src/states/StateGame.cpp create mode 100644 MyGame/src/states/StateSettings.cpp create mode 100644 TestBed/TestBed.cpp create mode 100644 TestBed/TestBed.vcxproj diff --git a/LifeAudioPlayer/Data.pak b/LifeAudioPlayer/Data.pak new file mode 100644 index 0000000..e69de29 diff --git a/LifeAudioPlayer/LifeAudioPlayer.cpp b/LifeAudioPlayer/LifeAudioPlayer.cpp new file mode 100644 index 0000000..6752de8 --- /dev/null +++ b/LifeAudioPlayer/LifeAudioPlayer.cpp @@ -0,0 +1,70 @@ +// LifeAudioPlayer.cpp : Defines the entry point for the console application. +// + +#include "stdafx.h" +#include +#include +#include +#include +#include +#include + +#include + +#include + +int _tmain(int argc, _TCHAR* argv[]) +{ + ResourceManager::me()->SetCentralDirectory(STR("C:\\development\\Programming\\Projects\\Data\\")); + + /*Core::IFile* txt = fileSystem->GetFile(STR("root/Data/TEST.txt")); + + Core::IDataStreamPtr stm1 = txt->OpenRead(); + Core::IDataStreamPtr stm2 = txt->OpenRead(); + + stm1->Seek(1); + stm2->Seek(1); + + char c1; + char c2; + + stm1->Read(&c1, 1); + stm2->Read(&c2, 1); + + printf("Stm1: %c; Stm2:%c\n", c1, c2); + + */ + //fileSystem->CreateFolder(STR("root/Data/OLOLO12345")); + fileSystem->NewFile(STR("root/Data/Sounds/ololo.wav"), 1024*1024*3); + + //Test(fileSystem); + + printf("File system ready!\n"); + + Sound::SoundSystem* sndSystem; + sndSystem = new Sound::SoundSystem(); + if(sndSystem->Init(fileSystem) != 0) + { + printf("Error initializing sound system!"); + return -1; + } + + printf("Sound system ready!\n"); + + Sound::ISound* snd = sndSystem->getSound(STR("root/Data/Sounds/Wwise_Nor_Music.015.ogg"), Sound::SM_ENABLED); + snd->Play(); + + //Sound::ISound* snd2 = sndSystem->getSound(STR("Wwise_Nor_Music.003.ogg"), Sound::SM_ENABLED); + //snd2->Play(); + + while(true) + { + sndSystem->Update(); + } + printf("Press Enter key to exit\n"); + getchar(); + + delete snd; + delete sndSystem; + return 0; +} \ No newline at end of file diff --git a/LifeAudioPlayer/LifeAudioPlayer.vcxproj b/LifeAudioPlayer/LifeAudioPlayer.vcxproj new file mode 100644 index 0000000..d2ba223 --- /dev/null +++ b/LifeAudioPlayer/LifeAudioPlayer.vcxproj @@ -0,0 +1,171 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {4F307D56-E0CB-442C-8E76-FB3C827D54BF} + Win32Proj + LifeAudioPlayer + <Project Location In Database> + <Source Control Database> + <Local Binding Root of Project> + Mercurial Source Control Package + $(VCTargetsPath11) + + + + Application + true + MultiByte + v120 + + + Application + true + MultiByte + v120 + + + Application + false + true + MultiByte + v120 + + + Application + false + true + MultiByte + v120 + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + C:\Program Files %28x86%29\Visual Leak Detector\include;$(SolutionDir)\LifeCore\include;$(SolutionDir)\LifeSound\include;%(AdditionalIncludeDirectories) + true + + + Console + true + C:\Program Files %28x86%29\Visual Leak Detector\lib;$(SolutionDir)\LifeCore\lib;$(SolutionDir)\LifeSound\lib;%(AdditionalLibraryDirectories) + LifeCore_d.lib;LifeSound_d.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + C:\Program Files %28x86%29\Visual Leak Detector\include;$(SolutionDir)\LifeCore\include;$(SolutionDir)\LifeSound\include;%(AdditionalIncludeDirectories) + true + + + Console + true + C:\Program Files %28x86%29\Visual Leak Detector\lib;$(SolutionDir)\LifeCore\lib;$(SolutionDir)\LifeSound\lib;%(AdditionalLibraryDirectories) + LifeCore_d.lib;LifeSound_d.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)\LifeCore\include;$(SolutionDir)\LifeSound\include;%(AdditionalIncludeDirectories) + + + Console + true + true + true + $(SolutionDir)\LifeCore\lib;$(SolutionDir)\LifeSound\lib;%(AdditionalLibraryDirectories) + LifeCore.lib;LifeSound.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)\LifeCore\include;$(SolutionDir)\LifeSound\include;%(AdditionalIncludeDirectories) + + + Console + true + true + true + $(SolutionDir)\LifeCore\lib;$(SolutionDir)\LifeSound\lib;%(AdditionalLibraryDirectories) + LifeCore.lib;LifeSound.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LifeAudioPlayer/stdafx.cpp b/LifeAudioPlayer/stdafx.cpp new file mode 100644 index 0000000..5ea3633 --- /dev/null +++ b/LifeAudioPlayer/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// LifeAudioPlayer.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file \ No newline at end of file diff --git a/LifeAudioPlayer/stdafx.h b/LifeAudioPlayer/stdafx.h new file mode 100644 index 0000000..fc834b9 --- /dev/null +++ b/LifeAudioPlayer/stdafx.h @@ -0,0 +1,13 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#include +#include + +// TODO: reference additional headers your program requires here \ No newline at end of file diff --git a/LifeAudioPlayer/targetver.h b/LifeAudioPlayer/targetver.h new file mode 100644 index 0000000..5b1f29c --- /dev/null +++ b/LifeAudioPlayer/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include \ No newline at end of file diff --git a/LifeCore/Array.cpp b/LifeCore/Array.cpp new file mode 100644 index 0000000..e69de29 diff --git a/LifeCore/Array.h b/LifeCore/Array.h new file mode 100644 index 0000000..7d6a848 --- /dev/null +++ b/LifeCore/Array.h @@ -0,0 +1,18 @@ +#ifndef Array_h__ +#define Array_h__ + +namespace LifeCore +{ + template + class Array + { + public: + Array(size_t capacity); + ~Array(); + T& operator[](size_t id) const; + size_t Write(const T* src, size_t count); + private: + T* data; + }; +} +#endif // Array_h__ \ No newline at end of file diff --git a/LifeCore/Axis.h b/LifeCore/Axis.h new file mode 100644 index 0000000..5328689 --- /dev/null +++ b/LifeCore/Axis.h @@ -0,0 +1,11 @@ +#ifndef Axis_h__ +#define Axis_h__ + +namespace LifeCore{ + enum Axis{ + AXIS_X, + AXIS_Y, + AXIS_Z + }; +} +#endif // Axis_h__ diff --git a/LifeCore/BoundingBox3D.h b/LifeCore/BoundingBox3D.h new file mode 100644 index 0000000..a430489 --- /dev/null +++ b/LifeCore/BoundingBox3D.h @@ -0,0 +1,170 @@ +#ifndef BoundingBox3D_h__ +#define BoundingBox3D_h__ + +#include "math/LifeMath.h" + +namespace LifeCore +{ +#undef INLINE +#ifdef _WIN32 +#ifdef _MSC_VER +#define INLINE __forceinline +#endif +#else +#ifdef __GNUC__ +#define INLINE __attribute__((always_inline)) +#endif +#endif + +#ifndef INLINE +#define INLINE inline +#endif + + enum ContainmentType + { + Disjoint = 0, + Contains = 1, + Intersects = 2 + }; + + class BoundingBox3D + { + public: + BoundingBox3D() + { + vMin = LifeMath::float3(1000000000, 1000000000, 1000000000); + vMax = LifeMath::float3(-1000000000, -1000000000, -1000000000); + } + INLINE BoundingBox3D& operator=(const BoundingBox3D& other) + { + vMin = other.minimum(); + vMax = other.maximum(); + return *this; + } + BoundingBox3D(float size) + { + float offset = size/2.0f; + vMin = LifeMath::float3(-offset, -offset, -offset); + vMax = LifeMath::float3(offset, offset, offset); + } + BoundingBox3D(LifeMath::float3& minPoint, LifeMath::float3& maxPoint) + { + vMin = minPoint; + vMax = maxPoint; + } + BoundingBox3D(LifeMath::float4& minPoint, LifeMath::float4& maxPoint) + { + vMin = LifeMath::float3(minPoint.X, minPoint.Y, minPoint.Z); + vMax = LifeMath::float3(maxPoint.X, maxPoint.Y, maxPoint.Z); + } + const LifeMath::float3 maximum() const + { + return vMax; + } + + LifeMath::float3 size() const + { + return vMax - vMin; + } + + const LifeMath::float3 minimum() const + { + return vMin; + } + void resize(const LifeMath::float3& min, const LifeMath::float3& max) + { + vMin = min; + vMax = max; + } + float sizeX() const + { + return vMax.X - vMin.X; + } + float sizeY() const + { + return vMax.Y - vMin.Y; + } + float sizeZ() const + { + return vMax.Z - vMin.Z; + } + ~BoundingBox3D() + { + } + void Merge(BoundingBox3D& box) + { + Vec3Maximize(this->vMax, box.maximum(), this->vMax); + Vec3Minimize(this->vMin, box.minimum(), this->vMin); + } + BoundingBox3D Clone() + { + return BoundingBox3D(vMin, vMax); + } + static BoundingBox3D FromPoints(LifeMath::float4* points, int count) + { + LifeMath::float4 min = points[0]; + LifeMath::float4 max = min; + for (int i = 0; i < count; i++) + { + Vec4Minimize(min, points[i], min); + Vec4Maximize(max, points[i], max); + } + return BoundingBox3D(min, max); + } + static BoundingBox3D FromPoints(LifeMath::float3* points, int count) + { + LifeMath::float3 min = points[0]; + LifeMath::float3 max = min; + for (int i = 0; i < count; i++) + { + Vec3Minimize(min, points[i], min); + Vec3Maximize(max, points[i], max); + } + return BoundingBox3D(min, max); + } + static ContainmentType ContainsBox( BoundingBox3D& box1, BoundingBox3D& box2) + { + if ((box1.maximum().X < box2.minimum().X) || (box1.minimum().X > box2.maximum().X)) + { + return Disjoint; + } + if ((box1.maximum().Y < box2.minimum().Y) || (box1.minimum().Y > box2.maximum().Y)) + { + return Disjoint; + } + if ((box1.maximum().Z < box2.minimum().Z) || (box1.minimum().Z > box2.maximum().Z)) + { + return Disjoint; + } + if ((((box1.minimum().X <= box2.minimum().X) && (box2.maximum().X <= box1.maximum().X)) && ((box1.minimum().Y <= box2.minimum().Y) && (box2.maximum().Y <= box1.maximum().Y))) && ((box1.minimum().Z <= box2.minimum().Z) && (box2.maximum().Z <= box1.maximum().Z))) + { + return Contains; + } + return Intersects; + } + void GetCorners(LifeMath::float3* corners) const + { + // 5________6 + // / / + // / / + // /________2/ + // 1 | 7 + // | | + // | | + // 0---------3 + corners[0] = LifeMath::float3(vMin.X, vMin.Y, vMin.Z); + corners[1] = LifeMath::float3(vMin.X, vMax.Y, vMin.Z); + corners[2] = LifeMath::float3(vMax.X, vMax.Y, vMin.Z); + corners[3] = LifeMath::float3(vMax.X, vMin.Y, vMin.Z); + + corners[4] = LifeMath::float3(vMin.X, vMin.Y, vMax.Z); + corners[5] = LifeMath::float3(vMin.X, vMax.Y, vMax.Z); + corners[6] = LifeMath::float3(vMax.X, vMax.Y, vMax.Z); + corners[7] = LifeMath::float3(vMax.X, vMin.Y, vMax.Z); + } + private: + LifeMath::float3 vMin; + LifeMath::float3 vMax; + }; +} +#endif // BoundingBox3D_h__ \ No newline at end of file diff --git a/LifeCore/Build.h b/LifeCore/Build.h new file mode 100644 index 0000000..49b2381 --- /dev/null +++ b/LifeCore/Build.h @@ -0,0 +1,10 @@ +#define ENGINE_VERSION 1 +//#define BUILD_STATIC + +#ifdef BUILD_STATIC +#define LIFE_EXPORTS +#else +#define LIFE_EXPORTS +#endif + +typedef unsigned char byte; \ No newline at end of file diff --git a/LifeCore/Com.h b/LifeCore/Com.h new file mode 100644 index 0000000..82c54f5 --- /dev/null +++ b/LifeCore/Com.h @@ -0,0 +1,22 @@ +#ifndef COM_H__ +#define COM_H__ + +#include "log/NLog.h" + +#define PPCAT_NX(A, B) A ## B +#define PPCAT(A, B) PPCAT_NX(A, B) + +#define SAFE_RELEASE(__PTR___) \ +if(__PTR___ != nullptr)\ + (__PTR___)->Release(); \ + + +#define FINAL_RELEASE(__PTR___) \ + if(__PTR___ != nullptr) {\ + auto __PTR___##release_return_val = (__PTR___)->Release(); \ + if(__PTR___##release_return_val != 0) {\ + LogInfo() << PPCAT("Final release failed on object ", #__PTR___) << " Refcount now = " << __PTR___##release_return_val; \ + }} + + +#endif \ No newline at end of file diff --git a/LifeCore/Delegate.h b/LifeCore/Delegate.h new file mode 100644 index 0000000..a0c8dfd --- /dev/null +++ b/LifeCore/Delegate.h @@ -0,0 +1,132 @@ +#ifndef DELEGATE_H +#define DELEGATE_H + +//Container interface +template +class IContainer { +public: + virtual void Call(Args...) {} + virtual ~IContainer() {} + IContainer *next; +}; + +//Container realization +template< typename T, typename M, typename... Args > class MContainer : public IContainer { +public: + MContainer(T* c, M m) : mClass(c), mMethod(m) {} + void Call(Args... args) { + (mClass->*mMethod)(args...); + } + +private: + T *mClass; + M mMethod; +}; + +template< typename M, typename... Args > class FContainer : public IContainer { +public: + FContainer(M m) : mMethod(m) {} + void Call(Args... args) { + (mMethod)(args...); + } + +private: + M mMethod; +}; + +//Delegate +template +class Delegate { +public: + Delegate() { + mContainerHead = new IContainer(); + mContainerTail = mContainerHead; + mContainerHead->next = 0; + } + ~Delegate() { + IContainer *container = mContainerHead; + while(container) { + IContainer *temp = container->next; + delete container; + container = temp; + } + } + + void Clear() { + IContainer *container = mContainerHead->next; + while(container) { + IContainer *temp = container->next; + delete container; + container = temp; + } + mContainerHead->next = 0; + mContainerTail = mContainerHead; + } + + template + void Connect(T *c, M m) { + mContainerTail->next = new MContainer< T, M, Args... >(c, m); + mContainerTail->next->next = 0; + mContainerTail = mContainerTail->next; + } + + template + void Connect(M m) { + mContainerTail->next = new FContainer< M, Args... >(m); + mContainerTail->next->next = 0; + mContainerTail = mContainerTail->next; + } + + template + void Disconnect(T *c, M m) { + IContainer *container = mContainerHead; + while(container->next) { + MContainer *temp = dynamic_cast*>(container->next); + + if(temp) { + if(container->next == mContainerTail) { + mContainerTail = container; + } + container->next = container->next->next; + delete temp; + break; + } + container = container->next; + } + } + + template + void Disconnect(M m) { + IContainer *container = mContainerHead; + while(container->next) { + FContainer *temp = dynamic_cast*>(container->next); + + if(temp) { + if(container->next == mContainerTail) { + mContainerTail = container; + } + container->next = container->next->next; + delete temp; + break; + } + container = container->next; + } + } + + void operator ()(Args... args) { + Call(args...); + } + + void Call(Args... args) { + IContainer *container = mContainerHead; + while(container) { + container->Call(args...); + container = container->next; + } + } +private: + IContainer *mContainerHead; + IContainer *mContainerTail; +}; + +#endif // DELEGATE_H \ No newline at end of file diff --git a/LifeCore/DisplayMode.h b/LifeCore/DisplayMode.h new file mode 100644 index 0000000..a595a38 --- /dev/null +++ b/LifeCore/DisplayMode.h @@ -0,0 +1,14 @@ +#ifndef DisplayMode_h__ +#define DisplayMode_h__ + +namespace LifeCore +{ + struct DisplayMode + { + int width; + int height; + int refreshRate; + int bpp; + }; +} +#endif // DisplayMode_h__ \ No newline at end of file diff --git a/LifeCore/Error.h b/LifeCore/Error.h new file mode 100644 index 0000000..6dc79e8 --- /dev/null +++ b/LifeCore/Error.h @@ -0,0 +1,20 @@ +#ifndef Error_h__ +#define Error_h__ + +enum ERROR_CODE +{ + EC_TEST = 0, +}; + +enum SUCCESS_CODE +{ + SC_OK = 0, + SC_FAIL = 1 +}; + +enum VFS_ERROR +{ + EC_UNKNOWN = -1, + EC_FILE_ALREADY_EXISTS = 1 +}; +#endif // Error_h__ \ No newline at end of file diff --git a/LifeCore/FrameCounter.cpp b/LifeCore/FrameCounter.cpp new file mode 100644 index 0000000..48e7266 --- /dev/null +++ b/LifeCore/FrameCounter.cpp @@ -0,0 +1,78 @@ +#include "FrameCounter.h" +#include "Timer.h" + +namespace LifeCore +{ + FrameCounter::FrameCounter() + :fps(0.0f), frame_range(50), avg_fps(0.0f), time_elapsed(0.0f), + frame_id(0), time_elapsed_avg(0.0f), buffer_pos(0) + { + timer = new Timer(); + prev_time = new float[frame_range]; + for(int i = 0; i < frame_range; i++) + prev_time[i] = 0.0f; + } + FrameCounter::~FrameCounter() + { + delete[] prev_time; + } + float FrameCounter::GetFps() + { + return fps; + } + float FrameCounter::GetFpsAvg() + { + return avg_fps; + } + float FrameCounter::GetTimeElapled() + { + return time_elapsed; + } + float FrameCounter::GetTimeElapsedAvg() + { + return time_elapsed_avg; + } + void FrameCounter::SetFrameRange(int range) + { + frame_range = range; + buffer_pos = 0; + if(prev_time!=0) + delete[] prev_time; + + prev_time = new float[frame_range]; + for(int i = 0; i < frame_range; i++) + prev_time[i] = 0.0f; + } + int FrameCounter::GetFrameId() + { + return frame_id; + } + void FrameCounter::OnFrameEnd() + { + timer->Stop(); + + //get frame time elapsed + time_elapsed = timer->getTimeElapsed(); + //store time elapsed in buffer + prev_time[frame_id % frame_range] = time_elapsed; + //fix offset + buffer_pos++; + if(buffer_pos >= frame_range) + buffer_pos = 0; + + fps = 1.0f / time_elapsed; + + //compute average time elapsed + float sum = 0.0f; + for(int i = 0; i < frame_range; i++) + sum += prev_time[i]; + + time_elapsed_avg = sum / (float)frame_range; + + avg_fps = 1.0f / time_elapsed_avg; + + frame_id++; + + timer->Start(); + } +} \ No newline at end of file diff --git a/LifeCore/FrameCounter.h b/LifeCore/FrameCounter.h new file mode 100644 index 0000000..ecfd239 --- /dev/null +++ b/LifeCore/FrameCounter.h @@ -0,0 +1,32 @@ +#ifndef FrameCounter_h__ +#define FrameCounter_h__ + +namespace LifeCore +{ + class Timer; + class FrameCounter + { + public: + FrameCounter(); + ~FrameCounter(); + int GetFrameId(); + float GetFps(); + float GetFpsAvg(); + float GetTimeElapled(); + float GetTimeElapsedAvg(); + void SetFrameRange(int range); + void OnFrameEnd(); + private: + float* prev_time; + int buffer_pos; + float fps; + float avg_fps; + float time_elapsed; + float time_elapsed_avg; + int frame_range; + int frame_id; + Timer* timer; + }; +} + +#endif // FrameCounter_h__ \ No newline at end of file diff --git a/LifeCore/ICamera.h b/LifeCore/ICamera.h new file mode 100644 index 0000000..b17df05 --- /dev/null +++ b/LifeCore/ICamera.h @@ -0,0 +1,14 @@ +#ifndef ICamera_h__ +#define ICamera_h__ + +#include "IDevice.h" +#include "NMath.h" + +using namespace NettleMath; + +namespace LifeCore { + struct ICamera : public IDevice { + + }; +} +#endif // ICamera_h__ diff --git a/LifeCore/IDataRectangle.h b/LifeCore/IDataRectangle.h new file mode 100644 index 0000000..c2a76c0 --- /dev/null +++ b/LifeCore/IDataRectangle.h @@ -0,0 +1,14 @@ +#ifndef IDATARECTANGLE_H__ +#define IDATARECTANGLE_H__ + +#include "IMemoryStream.h" + +namespace LifeCore +{ + typedef struct IDataRectangle + { + int pitch; + LifeCore::IMemoryStream data; + }IDataRectangle, *PIDataRectangle, **LPIDataRectangle; +} +#endif \ No newline at end of file diff --git a/LifeCore/IDataStream.h b/LifeCore/IDataStream.h new file mode 100644 index 0000000..223db48 --- /dev/null +++ b/LifeCore/IDataStream.h @@ -0,0 +1,24 @@ +#ifndef IDATASTREAM_H__ +#define IDATASTREAM_H__ +#include + +namespace LifeCore +{ + class IDataStream + { + public: + virtual ~IDataStream(){} + virtual void Close() = 0; + virtual size_t Read(void* pBuffer, size_t countBytes) = 0; + virtual size_t Write(const void* pBuffer, size_t countBytes) = 0; + virtual void Seek(size_t position) = 0; + virtual size_t Tell() = 0; + virtual size_t Length() = 0; + virtual bool CanRead() = 0; + virtual bool CanWrite() = 0; + }; + + typedef std::auto_ptr IDataStreamPtr; +} + +#endif \ No newline at end of file diff --git a/LifeCore/IDevice.h b/LifeCore/IDevice.h new file mode 100644 index 0000000..2548c4b --- /dev/null +++ b/LifeCore/IDevice.h @@ -0,0 +1,9 @@ +#ifndef IDevice_h__ +#define IDevice_h__ + +namespace LifeCore{ + struct IDevice{ + + }; +} +#endif // IDevice_h__ diff --git a/LifeCore/IImage.h b/LifeCore/IImage.h new file mode 100644 index 0000000..d91c1e2 --- /dev/null +++ b/LifeCore/IImage.h @@ -0,0 +1,14 @@ +#ifndef IImage_h__ +#define IImage_h__ + +#include "NMath.h" +using namespace NettleMath; + +namespace LifeCore { + struct IImage { + virtual int2 GetSize() = 0; + virtual int32 GetBpp() = 0; + virtual bool Read(int2 size, uint8* buffer) = 0; + }; +} +#endif // IImage_h__ diff --git a/LifeCore/IInputSystem.cpp b/LifeCore/IInputSystem.cpp new file mode 100644 index 0000000..5d427d5 --- /dev/null +++ b/LifeCore/IInputSystem.cpp @@ -0,0 +1,61 @@ +#include "IInputSystem.h" +#include "IInputListener.h" + +namespace LifeCore +{ + bool IInputSystem::IsListening(IInputListener* listener){ + for(auto val : listeners) + if(val == listener) + return true; + return false; + } + bool IInputSystem::RemoveListener(IInputListener* listener){ + auto it = listeners.begin(); + for(it = listeners.begin(); it != listeners.end(); ++it){ + if(*it == listener) + break; + } + + if(it == listeners.end()) + return false; + + listeners.erase(it); + + if(listener->GetInputSystem() == this){ + listener->SetInputSystem(nullptr); + } + return true; + } + void IInputSystem::AddListener(IInputListener* listener){ + if(IsListening(listener)) + return; + listeners.push_back(listener); + if(listener->GetInputSystem() != this) + listener->SetInputSystem(this); + } + bool IInputSystem::KeyPressed(Key key){ + return keyboard_keys[static_cast(key)]; + } + void IInputSystem::OnKeyPress(Key key, unsigned int text, const std::string& str){ + keyboard_keys[static_cast(key)] = true; + for(auto listener : listeners) + listener->KeyDown(key, text, str); + } + void IInputSystem::OnKeyRelease(Key key, unsigned int text, const std::string& str){ + keyboard_keys[static_cast(key)] = false; + for(auto listener : listeners) + listener->KeyUp(key, text, str); + } + void IInputSystem::OnMouseMove(int absX, int relX, int absY, int relY){ + for(auto listener : listeners) + listener->MouseMove(absX, relX, absY, relY); + } + void IInputSystem::OnMousePress(MouseKey button){ + for(auto listener : listeners) + listener->MousePressed(button); + } + void IInputSystem::OnMouseRelease(MouseKey button){ + for(auto listener : listeners) + listener->MouseReleased(button); + } +} \ No newline at end of file diff --git a/LifeCore/IInputSystem.h b/LifeCore/IInputSystem.h new file mode 100644 index 0000000..2c0962b --- /dev/null +++ b/LifeCore/IInputSystem.h @@ -0,0 +1,132 @@ +#ifndef IInputSystem_h__ +#define IInputSystem_h__ +#include "Types.h" +#include +#include +#include +#include + +namespace LifeCore +{ + class KeyCodes { + public: + enum Key { + UNKNOWN_KEY, + KEY_ESCAPE, + KEY_F1, + KEY_F2, + KEY_F3, + KEY_F4, + KEY_F5, + KEY_F6, + KEY_F7, + KEY_F8, + KEY_F9, + KEY_F10, + KEY_F11, + KEY_F12, + KEY_PRINT_SCREEN, + KEY_SCROLL_LOCK, + KEY_PAUSE, + + KEY_TILDE, + KEY_1, + KEY_2, + KEY_3, + KEY_4, + KEY_5, + KEY_6, + KEY_7, + KEY_8, + KEY_9, + KEY_0, + KEY_MINUS, + KEY_EQUAL, + KEY_BACKSPACE, + + KEY_TAB, + KEY_CAPS_LOCK, + KEY_SHIFT, + KEY_CONTROL, + KEY_META_LEFT, + KEY_META_RIGHT, + KEY_ALT, + KEY_SPACE, + KEY_MENU, + KEY_ENTER, + + KEY_A, + KEY_B, + KEY_C, + KEY_D, + KEY_E, + KEY_F, + KEY_G, + KEY_H, + KEY_I, + KEY_J, + KEY_K, + KEY_L, + KEY_M, + KEY_N, + KEY_O, + KEY_P, + KEY_Q, + KEY_R, + KEY_S, + KEY_T, + KEY_U, + KEY_V, + KEY_W, + KEY_X, + KEY_Y, + KEY_Z, + KEY_BRACKET_RIGHT, + KEY_BRACKET_LEFT, + KEY_BACKSLASH, + KEY_SEMICOLON, + KEY_APOSTROPHE, + KEY_COMMA, + KEY_DOT, + KEY_SLASH, + + KEY_INSERT, + KEY_DELETE, + KEY_HOME, + KEY_END, + KEY_PAGE_UP, + KEY_PAGE_DOWN, + + KEY_UP, + KEY_LEFT, + KEY_DOWN, + KEY_RIGHT, + + KEY_NUM_LOCK, + KEY_NUM_DIVIDE, + KEY_NUM_MULTIPLY, + KEY_NUM_SUBTRACT, + KEY_NUM_ADD, + KEY_NUM_DECIMAL, + KEY_NUM_0, + KEY_NUM_1, + KEY_NUM_2, + KEY_NUM_3, + KEY_NUM_4, + KEY_NUM_5, + KEY_NUM_SPECIAL, + KEY_NUM_6, + KEY_NUM_7, + KEY_NUM_8, + KEY_NUM_9 + }; + }; + + class IInputListener; + class IInputSystem + { + public: + virtual bool IsKeyPressed(KeyCodes::Key key) = 0; + }; +} +#endif // IInputSystem_h__ \ No newline at end of file diff --git a/LifeCore/IMemoryStream.h b/LifeCore/IMemoryStream.h new file mode 100644 index 0000000..5188d4e --- /dev/null +++ b/LifeCore/IMemoryStream.h @@ -0,0 +1,139 @@ +#ifndef I_MEMORY_STREAM_H__ +#define I_MEMORY_STREAM_H__ + +#include "IDataStream.h" + +namespace LifeCore +{ + class IMemoryStream : public LifeCore::IDataStream + { + private: + char* pData; + size_t position; + size_t stmLength; + bool userData; + public: + IMemoryStream() + { + userData = true; + pData = 0; + stmLength = 0; + position = 0; + } + IMemoryStream(size_t size) + { + userData = false; + pData = new char[size]; + stmLength = size; + position = 0; + } + IMemoryStream(char* _pData, size_t dataSize) + { + userData = true; + pData = _pData; + stmLength = dataSize; + position = 0; + } + ~IMemoryStream() + { + if(!userData && pData !=0) + { + delete[] pData; + } + } + int stride; + int vertexCount; + + char* GetData() + { + return pData; + } + void Clear(int val) + { + memset(pData, val, stmLength); + } + void Close() + { + } + void Reset(char* _pData, size_t dataSize) + { + pData = _pData; + stmLength = dataSize; + position = 0; + } + size_t Read(void* pBuffer, size_t countBytes) + { + size_t diff = stmLength - position; + if(diff <=0 || !countBytes)return 0; + if(countBytes > diff) + countBytes = diff; + memcpy(pBuffer, pData + position, countBytes); + position+=countBytes; + + return countBytes; + } + + size_t ReadInt8(char& data) + { + return Read(&data, sizeof(char)); + } + size_t ReadInt16(short& data) + { + return Read(&data, sizeof(short)); + } + size_t ReadInt32(int& data) + { + return Read(&data, sizeof(int)); + } + +#define DEF_WRITE(type_name)\ + size_t Write(type_name val) \ + { return Write(&val, sizeof(type_name)); } \ + + DEF_WRITE(short); + DEF_WRITE(int); + DEF_WRITE(char); + DEF_WRITE(float); + DEF_WRITE(double); + DEF_WRITE(long long); + DEF_WRITE(unsigned short); + DEF_WRITE(unsigned int); + DEF_WRITE(unsigned char); + + size_t Write(const void* pBuffer, size_t countBytes) + { + size_t diff = stmLength - position; + if(diff <=0)return 0; + //if(countBytes > diff) + // countBytes = diff; + memcpy(pData + position, pBuffer, countBytes); + position+=countBytes; + return countBytes; + } + void Seek(size_t _position) + { + position = _position; + if(position < 0) position = 0; + if(position >= stmLength && stmLength !=0) position = stmLength - 1; + } + + size_t Tell() + { + return position; + } + size_t Length() + { + return stmLength; + } + bool CanRead() + { + return true; + } + bool CanWrite() + { + return true; + } + }; +} + +#endif \ No newline at end of file diff --git a/LifeCore/IProperty.cpp b/LifeCore/IProperty.cpp new file mode 100644 index 0000000..461ee66 --- /dev/null +++ b/LifeCore/IProperty.cpp @@ -0,0 +1,24 @@ +#include "IProperty.h" + +namespace LifeCore +{ + bool IProperty::ContainsAttribute(const String& name) + { + if(attributes.find(name) == attributes.end()) + return false; + return true; + } + int IProperty::AddAttribute(const String& name, const String& value) + { + attributes[name] = value; + return SC_OK; + } + int IProperty::GetAttribute(const String& name, String& value) + { + if(attributes.find(name) == attributes.end()) + return SC_FAIL; + + value = attributes[name]; + return SC_OK; + } +} \ No newline at end of file diff --git a/LifeCore/IProperty.h b/LifeCore/IProperty.h new file mode 100644 index 0000000..b1f469f --- /dev/null +++ b/LifeCore/IProperty.h @@ -0,0 +1,57 @@ +#ifndef IProperty_h__ +#define IProperty_h__ + +#include "LString.h" +#include "delegate/Delegate.h" +#include +#include "Error.h" +#include "ISerializable.h" + +namespace LifeCore +{ + enum PropertyEditMode + { + EM_INPLACE = 0, + EM_DROPDOWN = 1, + EM_CUSTOM = 2, + }; + + struct Attribute + { + public: + String Name; + String Value; + }; + + class IProperty : public ISerializable + { + public: + EVENT0(PropertyEvent); + PropertyEvent Event_ValueChanged; + int AddAttribute(const String& name, const String& value); + int GetAttribute(const String& name, String& value); + virtual String ToString() { return STR("Unspecified"); }; + virtual void FromString(const String& str) { UNREFERENCED_PARAMETER(str); } + bool ContainsAttribute(const String& name); + template void Set(const T* src){ + Set(static_cast(src), sizeof(T)); + } + template void Set(const T& src){ + Set(static_cast(&src), sizeof(T)); + } + template const T& Get(){ + return *static_cast(GetPtr()); + } + private: + typedef std::map AttributeList; + typedef std::map::iterator AttributeListItr; + AttributeList attributes; + protected: + virtual void Set(const void* src, size_t size) = 0; + virtual const void* GetPtr() = 0; + void OnValueChanged(){ + Event_ValueChanged.Invoke(); + } + }; +} +#endif // IProperty_h__ \ No newline at end of file diff --git a/LifeCore/IPropertyHolder.cpp b/LifeCore/IPropertyHolder.cpp new file mode 100644 index 0000000..bc4840a --- /dev/null +++ b/LifeCore/IPropertyHolder.cpp @@ -0,0 +1,66 @@ +#include "IPropertyHolder.h" +#include +namespace LifeCore +{ + bool IPropertyHolder::Serialize(LifeCore::IMemoryStream* stm) + { + stm->Write(size_t(properties.size())); + for(PropertyCollectionItr it = properties.begin(); it != properties.end(); ++it){ + (*it)->Serialize(stm); + } + return true; + } + bool IPropertyHolder::Deserialize(LifeCore::IMemoryStream* stm) + { + size_t prop_cnt; + stm->Read(&prop_cnt, sizeof(prop_cnt)); + assert(prop_cnt == properties.size() && "Failed on IPropertyHolder deserialization!"); + for(PropertyCollectionItr it = properties.begin(); it != properties.end(); ++it){ + (*it)->Deserialize(stm); + } + return true; + } + PropertyCollection* IPropertyHolder::GetProperties() + { + return &properties; + } + void IPropertyHolder::RegisterProperty(IProperty* property) + { + properties.push_back(property); + } + void IPropertyHolder::UnregisterProperty(IProperty* property) + { + UNREFERENCED_PARAMETER(property); + //TODO remove prop + } + bool IPropertyHolder::GetPropertyByAttribute(const String& name, IProperty** prop) + { + for(PropertyCollectionItr it = properties.begin(); it != properties.end(); ++it) + { + IProperty* p = *it; + if(p->ContainsAttribute(name)) + { + *prop = p; + return true; + } + } + return false; + } + bool IPropertyHolder::GetPropertyByAttribute(const String& name, const String& value, IProperty** prop) + { + for(PropertyCollectionItr it = properties.begin(); it != properties.end(); ++it) + { + IProperty* p = *it; + String tmpVal; + if(p->GetAttribute(name, tmpVal) == SC_OK) + { + if(tmpVal == value) + { + *prop = p; + return true; + } + } + } + return false; + } +} \ No newline at end of file diff --git a/LifeCore/IPropertyHolder.h b/LifeCore/IPropertyHolder.h new file mode 100644 index 0000000..c94d07b --- /dev/null +++ b/LifeCore/IPropertyHolder.h @@ -0,0 +1,27 @@ +#ifndef IPropertyHolder_h__ +#define IPropertyHolder_h__ + +#include +#include "IProperty.h" +#include "ISerializable.h" + +typedef std::vector PropertyCollection; +typedef std::vector::iterator PropertyCollectionItr; + +namespace LifeCore +{ + class IPropertyHolder : public ISerializable + { + public: + bool Serialize(LifeCore::IMemoryStream* stm); + bool Deserialize(LifeCore::IMemoryStream* stm); + PropertyCollection* GetProperties(); + void RegisterProperty(IProperty* property); + void UnregisterProperty(IProperty* property); + bool GetPropertyByAttribute(const String& name, IProperty** prop); + bool GetPropertyByAttribute(const String& name, const String& value, IProperty** prop); + private: + PropertyCollection properties; + }; +} +#endif // IPropertyHolder_h__ \ No newline at end of file diff --git a/LifeCore/IPropertyItem.h b/LifeCore/IPropertyItem.h new file mode 100644 index 0000000..6f0cbb0 --- /dev/null +++ b/LifeCore/IPropertyItem.h @@ -0,0 +1,25 @@ +#ifndef IPropertyItem_h__ +#define IPropertyItem_h__ + +#include "Property.h" +#include + +namespace LifeCore +{ + class IPropertyItem + { + public: + EVENT0(PropertyItemEvent); + PropertyItemEvent Event_ValueChanged; + virtual PropertyEditMode GetEditMode() = 0; + virtual void OnBeginEdit() {}; + virtual void OnEndEdit(const String& data) = 0; + virtual String ToString() = 0; + private: + void OnPropertyChanged() + { + Event_ValueChanged.Invoke(); + } + }; +} +#endif // IPropertyItem_h__ \ No newline at end of file diff --git a/LifeCore/ISerializable.h b/LifeCore/ISerializable.h new file mode 100644 index 0000000..1cebb57 --- /dev/null +++ b/LifeCore/ISerializable.h @@ -0,0 +1,13 @@ +#ifndef ISerializable_h__ +#define ISerializable_h__ + +#include "IMemoryStream.h" + +class ISerializable +{ +public: + virtual bool Serialize(LifeCore::IMemoryStream* stm) = 0; + virtual bool Deserialize(LifeCore::IMemoryStream* stm) = 0; +}; + +#endif // ISerializable_h__ \ No newline at end of file diff --git a/LifeCore/ITransform.h b/LifeCore/ITransform.h new file mode 100644 index 0000000..5a8136e --- /dev/null +++ b/LifeCore/ITransform.h @@ -0,0 +1,114 @@ +#ifndef ITransform_h__ +#define ITransform_h__ +#include "math/LifeMath.h" +#include "delegate/Delegate.h" + +namespace LifeCore +{ + class ITransform + { + private: + LifeMath::float3 position; + LifeMath::Quaternion rotation; + LifeMath::float3 scaling; + LifeMath::float4x4 transform; + bool valid; + + public: + EVENT(TransformEvent); + TransformEvent Event_SetPosition; + TransformEvent Event_SetRotation; + TransformEvent Event_SetScaling; + + void UpdateTransform() + { + LifeMath::float4x4 trans; + Mat4x4Translation(position, trans); + + LifeMath::float4x4 scale; + Mat4x4Scaling(scaling, scale); + + LifeMath::float4x4 rot; + Mat4x4RotationQuaternion(rotation, rot); + + transform = scale * rot * trans; + valid = true; + } + bool IsValid() + { + return valid; + } + ITransform() + :valid(false), position(0.0f, 0.0f, 0.0f), scaling(1.0f, 1.0f, 1.0f) + { + QuatSetAxisAngle(LifeMath::float3(0.0f, 0.0f, 0.0f), 0.0f, rotation); + UpdateTransform(); + } + ~ITransform() + { + } + const LifeMath::float4x4& GetMatrix() + { + if(!valid) + UpdateTransform(); + return transform; + } + const LifeMath::float3& GetPosition() const + { + return position; + } + const LifeMath::float3& GetScaling() const + { + return scaling; + } + const LifeMath::Quaternion& GetRotation() const + { + return rotation; + } + + void SetMatrix(const LifeMath::float4x4& newTransform) + { + transform = newTransform; + Mat4x4Decompose(transform, position, scaling, rotation); + valid = true; + } + void SetPosition(const LifeMath::float3& _position) + { + if(position != _position) + { + position = _position; + valid = false; + Event_SetPosition.Invoke(); + } + } + void SetRotation(const LifeMath::Quaternion& _rotation) + { + if(rotation != _rotation) + { + rotation = _rotation; + valid = false; + Event_SetRotation.Invoke(); + } + } + void SetRotation(const LifeMath::float3& axis, float angle) + { + QuatSetAxisAngle(axis, angle, rotation); + valid = false; + Event_SetRotation.Invoke(); + } + void SetAngles(const LifeMath::float3& angles) + { + printf("OMG! SetAngles isn't implemented!!"); + } + void SetScaling(const LifeMath::float3& _scaling) + { + if(scaling != _scaling) + { + scaling = _scaling; + valid = false; + Event_SetScaling.Invoke(); + } + } + }; +} +#endif // ITransform_h__ \ No newline at end of file diff --git a/LifeCore/IWindow.h b/LifeCore/IWindow.h new file mode 100644 index 0000000..ad09582 --- /dev/null +++ b/LifeCore/IWindow.h @@ -0,0 +1,43 @@ +#ifndef IRenderWindow_h__ +#define IRenderWindow_h__ + +#include "delegate/Delegate.h" +#include "Build.h" +#include "LString.h" +#include "DisplayMode.h" +#include "IWindowDemander.h" +#include "IInputSystem.h" + +namespace LifeCore +{ + class IWindow : public IInputSystem + { + public: + virtual void Update() = 0; + virtual void SetCaption(const String& caption) = 0; + virtual int GetWindowHandle() = 0; + virtual int GetWidth() = 0; + virtual int GetHeight() = 0; + virtual void SetSize(int w, int h) = 0; + virtual const String& GetCaption() = 0; + virtual bool IsFullScreen() = 0; + virtual void SetFullScreen(bool state) = 0; + virtual bool SwitchMode(bool fullscreen) = 0; + virtual void SetFullscreenMode(DisplayMode mode) = 0; + + /*virtual const DisplayMode& GetFullscreenMode() + { + return fsMode; + }*/ + virtual void OnActivate() = 0; + virtual void OnDeactivate() = 0; + virtual void OnResize() = 0; + virtual bool IsActive() = 0; + virtual bool IsModeSwitching() = 0; + virtual void SetCursorVisible(bool state) = 0; + virtual bool IsCursorVisible() = 0; + virtual void CenterWindow() = 0; + }; +} + +#endif // IRenderWindow_h__ \ No newline at end of file diff --git a/LifeCore/IWindowDemander.h b/LifeCore/IWindowDemander.h new file mode 100644 index 0000000..d4a3150 --- /dev/null +++ b/LifeCore/IWindowDemander.h @@ -0,0 +1,10 @@ +#ifndef IWindowDemander_h__ +#define IWindowDemander_h__ + +namespace LifeCore { + class IWindowDemander { + public: + virtual void OnRawInput() = 0; + }; +} +#endif // IWindowDemander_h__ diff --git a/LifeCore/InputDevice.h b/LifeCore/InputDevice.h new file mode 100644 index 0000000..b8e003e --- /dev/null +++ b/LifeCore/InputDevice.h @@ -0,0 +1,11 @@ +#ifndef InputDevice_h__ +#define InputDevice_h__ + +#include "IDevice.h" + +namespace LifeCore{ + class InputDevice : public LifeCore::IDevice{ + + }; +} +#endif // InputDevice_h__ diff --git a/LifeCore/InputListener222.h b/LifeCore/InputListener222.h new file mode 100644 index 0000000..54472f7 --- /dev/null +++ b/LifeCore/InputListener222.h @@ -0,0 +1,17 @@ +#ifndef InputListener_h__ +#define InputListener_h__ + +#include "InputTypes.h" +#include "math/LifeMath.h" + +namespace LifeCore{ + class InputListener{ + public: + InputListener(); + virtual ~InputListener(); + virtual void onKeyEvent(InputDeviceType dev_type, int dev_id, InputKey key, InputKeyAction action) = 0; + virtual void onAxisEvent(InputDeviceType dev_type, int dev_id, Axis axis, float value) = 0; + virtual void onAngleEvent(InputDeviceType dev_type, int dev_id, LifeMath::float3 axis, float angle) = 0; + }; +} +#endif // InputListener_h__ diff --git a/LifeCore/InputSystem.cpp b/LifeCore/InputSystem.cpp new file mode 100644 index 0000000..56936ff --- /dev/null +++ b/LifeCore/InputSystem.cpp @@ -0,0 +1,101 @@ +#include "InputSystem.h" +#include "log/NLog.h" + + +namespace LifeCore { + InputSystem::InputSystem() { + auto hInst = GetModuleHandle(NULL); + hwnd = NULL; + + WNDCLASSEX wndClass = { NULL }; + + wndClass.cbSize = sizeof(WNDCLASSEX); + wndClass.cbClsExtra = NULL; + wndClass.cbWndExtra = NULL; + wndClass.hbrBackground = (HBRUSH)GetStockObject(1); + wndClass.hCursor = (HCURSOR)LoadCursor(NULL, IDC_ARROW); + wndClass.hIcon = LoadIcon(hInst, IDI_APPLICATION); + wndClass.hIconSm = LoadIcon(hInst, IDI_APPLICATION); + wndClass.hInstance = hInst; + wndClass.lpfnWndProc = WndProc; + wndClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wndClass.lpszMenuName = NULL; + wndClass.lpszClassName = _text("LifeEngineInputWindow"); + + if(!RegisterClassEx(&wndClass)) { + MessageBox((HWND)hwnd, _text("Can not create window class! We all gonna die!!"), _text("OH SHIT!"), MB_OK); + } + + RECT NewWindowSize; + NewWindowSize.top = 30; + NewWindowSize.left = 300; + NewWindowSize.bottom = NewWindowSize.top + 100; + NewWindowSize.right = NewWindowSize.left + 100; + + AdjustWindowRect(&NewWindowSize, WS_OVERLAPPEDWINDOW, false); + + + if(!(hwnd = (long)CreateWindowEx(NULL, _text("LifeEngineInputWindow"), _text("LifeEngine Input window"), WS_THICKFRAME, + NewWindowSize.left, NewWindowSize.top, NewWindowSize.right - NewWindowSize.left, NewWindowSize.bottom - NewWindowSize.top, NULL, NULL, hInst, NULL))) { + MessageBox((HWND)hwnd, _text("Can not create window! We all gonna die!!"), _text("HOLY SHIT!"), MB_OK); + } + + SetWindowLongPtr((HWND)hwnd, GWLP_USERDATA, (long)(this)); + ShowWindow((HWND)hwnd, SW_HIDE); + } + + void InputSystem::Init() { + RAWINPUTDEVICE dev; + dev.usUsagePage = 1; + dev.usUsage = 2; + dev.dwFlags = 0; + dev.hwndTarget = (HWND)hwnd; + + if(!RegisterRawInputDevices(&dev, 1, sizeof(dev))) { + LogInfo() << "Raw devices registration failed!"; + } else { + LogInfo() << "Raw devices registered!"; + } + } + + void InputSystem::Update() { + MSG msg = { 0 }; + while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + LRESULT __stdcall InputSystem::WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { + switch(message) { + case WM_INPUT: + { + UINT dwSize; + GetRawInputData((HRAWINPUT)lparam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER)); + char* data = new char[dwSize]; + + if(GetRawInputData((HRAWINPUT)lparam, RID_INPUT, data, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize) { + LogInfo() << "Wrong raw input size!"; + } + + RAWINPUT* inp = (RAWINPUT*)data; + if(inp->header.dwType == RIM_TYPEMOUSE) { + LogInfo() << "Read mouse input2!"; + LogInfo() << "Buttons: " << inp->data.mouse.ulButtons; + } + + long val = GetWindowLongPtr(hwnd, GWLP_USERDATA); + if(val) { + InputSystem* input = reinterpret_cast(val); + LogInfo() << "OnInput"; + //wnd->OnInput(); + } + + delete[] data; + return 0; + } + break; + } + return DefWindowProc(hwnd, message, wparam, lparam); + } +} \ No newline at end of file diff --git a/LifeCore/InputSystem.h b/LifeCore/InputSystem.h new file mode 100644 index 0000000..ba4f574 --- /dev/null +++ b/LifeCore/InputSystem.h @@ -0,0 +1,19 @@ +#ifndef InputSystem_h__ +#define InputSystem_h__ +#include +#include "Types.h" + +namespace LifeCore { + class InputSystem + { + public: + InputSystem(); + void Init(); + void Update(); + private: + static long __stdcall WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); + uint8 keys[0xff]; + long hwnd; + }; +} +#endif // InputSystem_h__ diff --git a/LifeCore/InputSystem222.h b/LifeCore/InputSystem222.h new file mode 100644 index 0000000..15c8143 --- /dev/null +++ b/LifeCore/InputSystem222.h @@ -0,0 +1,22 @@ +#ifndef InputSystem_h__ +#define InputSystem_h__ + +#include "InputDevice.h" +#include "InputTypes.h" +#include +#include + + +namespace LifeCore{ + class InputSystem{ + public: + typedef std::vector > InputDeviceList; + InputSystem(); + virtual ~InputSystem(); + InputDeviceList getDevices(InputDeviceType device); + virtual void update(); + private: + InputDeviceList devices; + }; +} +#endif // InputSystem_h__ diff --git a/LifeCore/InputSystemOIS22.cpp b/LifeCore/InputSystemOIS22.cpp new file mode 100644 index 0000000..ec58bb9 --- /dev/null +++ b/LifeCore/InputSystemOIS22.cpp @@ -0,0 +1,181 @@ +#include "InputSystemOIS.h" + +#include +using namespace OIS; + +#include +#include +#include +#include +#include "LString.h" + +namespace LifeCore +{ + class input_impl : public KeyListener, MouseListener + { + public: + input_impl(IInputSystem* sys) + { + input_system = sys; + } + ~input_impl() + { + if(input) + InputManager::destroyInputSystem(input); + } + void Init(int scrW, int scrH, bool exclusiveMode, int hwnd) + { + //setlocale(LC_ALL,""); + HWND hw = (HWND)hwnd; + std::ostringstream wnd; + wnd << (size_t)hw << "\0"; + pl.insert(std::make_pair( std::string("WINDOW"), wnd.str() )); + + if( exclusiveMode) + { + pl.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_FOREGROUND"))); + pl.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_EXCLUSIVE"))); + } + else + { + pl.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_BACKGROUND"))); + pl.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_NONEXCLUSIVE"))); + } + pl.insert(std::make_pair(std::string("w32_keyboard"), std::string("DISCL_BACKGROUND"))); + pl.insert(std::make_pair(std::string("w32_keyboard"), std::string("DISCL_NONEXCLUSIVE"))); + + input = InputManager::createInputSystem(pl); + keyboard = (Keyboard*)input->createInputObject(OISKeyboard,1); + mouse = (Mouse*)input->createInputObject(OISMouse,1); + mouse->getMouseState().width = scrW; + mouse->getMouseState().height = scrH; + + keyboard->setEventCallback(this); + mouse->setEventCallback(this); + keyboard->setTextTranslation(OIS::Keyboard::Unicode); + } + + bool KeyPressed(Key key) + { + return keyboard->isKeyDown((KeyCode)key); + } + bool MouseButtonPressed(LifeCore::MouseKey button) + { + MouseButtonID k = (MouseButtonID)(button); + return mState.buttonDown(k); + } + int GetMouseWheel() + { + return mState.Z.rel; + } + void Capture() + { + keyboard->capture(); + mouse->capture(); + + mState = mouse->getMouseState(); + } + int GetMouseRelativeX() + { + return mState.X.rel; + } + int GetMouseRelativeY() + { + return mState.Y.rel; + } + int GetMouseAbsoluteX() + { + return mState.X.abs; + } + int GetMouseAbsoluteY() + { + return mState.Y.abs; + } + private: + IInputSystem* input_system; + ParamList pl; + InputManager* input; + Keyboard* keyboard; + Mouse* mouse; + OIS::MouseState mState; + bool keyPressed( const KeyEvent &arg ){ + //String text = arg.text; + wchar_t new_key[2]; + new_key[0]= arg.text; + new_key[1] = L'\0'; + std::wstring str(&new_key[0]); + std::string res; + WCSTMBS(str.c_str(), res); + input_system->OnKeyPress((Key)arg.key,new_key[0], res); + return true; + } + bool keyReleased( const KeyEvent &arg ){ + wchar_t new_key[2]; + new_key[0] = arg.text; + new_key[1] = L'\0'; + std::wstring str(&new_key[0]); + std::string res; + WCSTMBS(str.c_str(), res); + input_system->OnKeyRelease((Key)arg.key, arg.text, res); + return true; + } + bool mouseMoved( const MouseEvent &arg ) { + input_system->OnMouseMove(arg.state.X.abs,arg.state.X.rel,arg.state.Y.abs,arg.state.Y.rel); + return true; + } + bool mousePressed( const MouseEvent &arg, MouseButtonID id ){ + input_system->OnMousePress((LifeCore::MouseKey)id); + return true; + } + bool mouseReleased( const MouseEvent &arg, MouseButtonID id ){ + input_system->OnMouseRelease((LifeCore::MouseKey)id); + return true; + } + }; + + InputSystemOIS::InputSystemOIS() + { + pImpl = new input_impl(this); + } + InputSystemOIS::~InputSystemOIS() + { + if(pImpl) + delete pImpl; + } + void InputSystemOIS::Init(int scrW, int scrH, bool exclusiveMode, int hwnd) + { + pImpl->Init(scrW, scrH, exclusiveMode, hwnd); + } + void InputSystemOIS::Capture() + { + pImpl->Capture(); + } + bool InputSystemOIS::KeyPressed(Key key) + { + return pImpl->KeyPressed(key); + } + bool InputSystemOIS::MouseButtonPressed(LifeCore::MouseKey key) + { + return pImpl->MouseButtonPressed(key); + } + int InputSystemOIS::GetMouseWheel() + { + return pImpl->GetMouseWheel(); + } + int InputSystemOIS::GetMouseRelativeX() + { + return pImpl->GetMouseRelativeX(); + } + int InputSystemOIS::GetMouseRelativeY() + { + return pImpl->GetMouseRelativeY(); + } + int InputSystemOIS::GetMouseAbsoluteX() + { + return pImpl->GetMouseAbsoluteX(); + } + int InputSystemOIS::GetMouseAbsoluteY() + { + return pImpl->GetMouseAbsoluteY(); + } +} \ No newline at end of file diff --git a/LifeCore/InputSystemOIS22.h b/LifeCore/InputSystemOIS22.h new file mode 100644 index 0000000..cab0199 --- /dev/null +++ b/LifeCore/InputSystemOIS22.h @@ -0,0 +1,30 @@ +#ifndef InputSystemOIS_h__ +#define InputSystemOIS_h__ + +#include "IInputSystem.h" + +namespace LifeCore +{ + class input_impl; + class InputSystemOIS : public LifeCore::IInputSystem + { + private: + input_impl* pImpl; + public: + InputSystemOIS(); + ~InputSystemOIS(); + void Init(int scrW, int scrH, bool exclusiveMode, int hwnd); + void Capture(); + bool KeyPressed(LifeCore::Key key); + bool MouseButtonPressed(LifeCore::MouseKey key); + int GetMouseWheel(); + int GetMouseRelativeX(); + int GetMouseRelativeY(); + int GetMouseAbsoluteX(); + int GetMouseAbsoluteY(); + void CenterCursor(){} + }; + +}; + +#endif // InputSystemOIS_h__ \ No newline at end of file diff --git a/LifeCore/InputTypes222.h b/LifeCore/InputTypes222.h new file mode 100644 index 0000000..e8794ef --- /dev/null +++ b/LifeCore/InputTypes222.h @@ -0,0 +1,182 @@ +#ifndef InputTypes_h__ +#define InputTypes_h__ + +#include "Axis.h" + +namespace LifeCore{ + + enum InputKey { + KEY_UNASSIGNED = 0x00, + KEY_ESCAPE = 0x01, + KEY_1 = 0x02, + KEY_2 = 0x03, + KEY_3 = 0x04, + KEY_4 = 0x05, + KEY_5 = 0x06, + KEY_6 = 0x07, + KEY_7 = 0x08, + KEY_8 = 0x09, + KEY_9 = 0x0A, + KEY_0 = 0x0B, + KEY_MINUS = 0x0C, // - on main keyboard + KEY_EQUALS = 0x0D, + KEY_BACK = 0x0E, // backspace + KEY_TAB = 0x0F, + KEY_Q = 0x10, + KEY_W = 0x11, + KEY_E = 0x12, + KEY_R = 0x13, + KEY_T = 0x14, + KEY_Y = 0x15, + KEY_U = 0x16, + KEY_I = 0x17, + KEY_O = 0x18, + KEY_P = 0x19, + KEY_LBRACKET = 0x1A, + KEY_RBRACKET = 0x1B, + KEY_RETURN = 0x1C, // Enter on main keyboard + KEY_LCONTROL = 0x1D, + KEY_A = 0x1E, + KEY_S = 0x1F, + KEY_D = 0x20, + KEY_F = 0x21, + KEY_G = 0x22, + KEY_H = 0x23, + KEY_J = 0x24, + KEY_K = 0x25, + KEY_L = 0x26, + KEY_SEMICOLON = 0x27, + KEY_APOSTROPHE = 0x28, + KEY_GRAVE = 0x29, // accent + KEY_LSHIFT = 0x2A, + KEY_BACKSLASH = 0x2B, + KEY_Z = 0x2C, + KEY_X = 0x2D, + KEY_C = 0x2E, + KEY_V = 0x2F, + KEY_B = 0x30, + KEY_N = 0x31, + KEY_M = 0x32, + KEY_COMMA = 0x33, + KEY_PERIOD = 0x34, // . on main keyboard + KEY_SLASH = 0x35, // / on main keyboard + KEY_RSHIFT = 0x36, + KEY_MULTIPLY = 0x37, // * on numeric keypad + KEY_LMENU = 0x38, // left Alt + KEY_SPACE = 0x39, + KEY_CAPITAL = 0x3A, + KEY_F1 = 0x3B, + KEY_F2 = 0x3C, + KEY_F3 = 0x3D, + KEY_F4 = 0x3E, + KEY_F5 = 0x3F, + KEY_F6 = 0x40, + KEY_F7 = 0x41, + KEY_F8 = 0x42, + KEY_F9 = 0x43, + KEY_F10 = 0x44, + KEY_NUMLOCK = 0x45, + KEY_SCROLL = 0x46, // Scroll Lock + KEY_NUMPAD7 = 0x47, + KEY_NUMPAD8 = 0x48, + KEY_NUMPAD9 = 0x49, + KEY_SUBTRACT = 0x4A, // - on numeric keypad + KEY_NUMPAD4 = 0x4B, + KEY_NUMPAD5 = 0x4C, + KEY_NUMPAD6 = 0x4D, + KEY_ADD = 0x4E, // + on numeric keypad + KEY_NUMPAD1 = 0x4F, + KEY_NUMPAD2 = 0x50, + KEY_NUMPAD3 = 0x51, + KEY_NUMPAD0 = 0x52, + KEY_DECIMAL = 0x53, // . on numeric keypad + KEY_OEM_102 = 0x56, // < > | on UK/Germany keyboards + KEY_F11 = 0x57, + KEY_F12 = 0x58, + KEY_F13 = 0x64, // (NEC PC98) + KEY_F14 = 0x65, // (NEC PC98) + KEY_F15 = 0x66, // (NEC PC98) + KEY_KANA = 0x70, // (Japanese keyboard) + KEY_ABNT_C1 = 0x73, // / ? on Portugese (Brazilian) keyboards + KEY_CONVERT = 0x79, // (Japanese keyboard) + KEY_NOCONVERT = 0x7B, // (Japanese keyboard) + KEY_YEN = 0x7D, // (Japanese keyboard) + KEY_ABNT_C2 = 0x7E, // Numpad . on Portugese (Brazilian) keyboards + KEY_NUMPADEQUALS= 0x8D, // = on numeric keypad (NEC PC98) + KEY_PREVTRACK = 0x90, // Previous Track (KEY_CIRCUMFLEX on Japanese keyboard) + KEY_AT = 0x91, // (NEC PC98) + KEY_COLON = 0x92, // (NEC PC98) + KEY_UNDERLINE = 0x93, // (NEC PC98) + KEY_KANJI = 0x94, // (Japanese keyboard) + KEY_STOP = 0x95, // (NEC PC98) + KEY_AX = 0x96, // (Japan AX) + KEY_UNLABELED = 0x97, // (J3100) + KEY_NEXTTRACK = 0x99, // Next Track + KEY_NUMPADENTER = 0x9C, // Enter on numeric keypad + KEY_RCONTROL = 0x9D, + KEY_MUTE = 0xA0, // Mute + KEY_CALCULATOR = 0xA1, // Calculator + KEY_PLAYPAUSE = 0xA2, // Play / Pause + KEY_MEDIASTOP = 0xA4, // Media Stop + KEY_VOLUMEDOWN = 0xAE, // Volume - + KEY_VOLUMEUP = 0xB0, // Volume + + KEY_WEBHOME = 0xB2, // Web home + KEY_NUMPADCOMMA = 0xB3, // , on numeric keypad (NEC PC98) + KEY_DIVIDE = 0xB5, // / on numeric keypad + KEY_SYSRQ = 0xB7, + KEY_RMENU = 0xB8, // right Alt + KEY_PAUSE = 0xC5, // Pause + KEY_HOME = 0xC7, // Home on arrow keypad + KEY_UP = 0xC8, // UpArrow on arrow keypad + KEY_PGUP = 0xC9, // PgUp on arrow keypad + KEY_LEFT = 0xCB, // LeftArrow on arrow keypad + KEY_RIGHT = 0xCD, // RightArrow on arrow keypad + KEY_END = 0xCF, // End on arrow keypad + KEY_DOWN = 0xD0, // DownArrow on arrow keypad + KEY_PGDOWN = 0xD1, // PgDn on arrow keypad + KEY_INSERT = 0xD2, // Insert on arrow keypad + KEY_DELETE = 0xD3, // Delete on arrow keypad + KEY_LWIN = 0xDB, // Left Windows key + KEY_RWIN = 0xDC, // Right Windows key + KEY_APPS = 0xDD, // AppMenu key + KEY_POWER = 0xDE, // System Power + KEY_SLEEP = 0xDF, // System Sleep + KEY_WAKE = 0xE3, // System Wake + KEY_WEBSEARCH = 0xE5, // Web Search + KEY_WEBFAVORITES= 0xE6, // Web Favorites + KEY_WEBREFRESH = 0xE7, // Web Refresh + KEY_WEBSTOP = 0xE8, // Web Stop + KEY_WEBFORWARD = 0xE9, // Web Forward + KEY_WEBBACK = 0xEA, // Web Back + KEY_MYCOMPUTER = 0xEB, // My Computer + KEY_MAIL = 0xEC, // Mail + KEY_MEDIASELECT = 0xED, // Media Select + + KEY_MOUSE_OFFSET = 0xff, + + KEY_MOUSE_LEFT = KEY_MOUSE_OFFSET + 0, + KEY_MOUSE_RIGHT = KEY_MOUSE_OFFSET + 1, + KEY_MOUSE_MIDDLE = KEY_MOUSE_OFFSET + 2, + KEY_MOUSE_3 = KEY_MOUSE_OFFSET + 3, + KEY_MOUSE_4 = KEY_MOUSE_OFFSET + 4, + KEY_MOUSE_5 = KEY_MOUSE_OFFSET + 5, + KEY_MOUSE_6 = KEY_MOUSE_OFFSET + 6, + KEY_MOUSE_7 =KEY_MOUSE_OFFSET + 7, + }; + + enum InputKeyAction{ + IKA_PRESS, + IKA_RELEASE, + IKA_HOLD + }; + + enum InputDeviceType{ + IDT_UNKNOWN = 0, + IDT_KEYBOARD = (1 << 0), + IDT_MOUSE = (1 << 1), + IDT_JOYSTICK = (1 << 2), + IDT_MAGNETOMETER = (1 << 3), + IDT_GYROSCOPE = (1 << 4) + }; +} +#endif // InputTypes_h__ diff --git a/LifeCore/LString.cpp b/LifeCore/LString.cpp new file mode 100644 index 0000000..7d7dd9a --- /dev/null +++ b/LifeCore/LString.cpp @@ -0,0 +1,209 @@ +#include "LString.h" +#include + +extern int StrToInt(const String& str) +{ + std::stringstream ss(str); + int val; + ss >> val; + return val; +} + +extern String GetFileName(const String& path) +{ + int a = path.find_last_of(PATH_SPLITTER); + if(a == -1) + return path; + int length = path.length(); + return path.substr(a+1, length - a); +} + +extern String GetFileName(const String& path, const String& pathSplitter) +{ + int a = path.find_last_of(pathSplitter.c_str()); + if(a == -1) + return path; + int length = path.length(); + return path.substr(a+1, length - a); +} + +extern String GetFilePath(const String& path, const String& pathSplitter) +{ + int a = path.find_last_of(pathSplitter.c_str()); + if(a == -1) + return path; + return path.substr(0, a); +} + +extern String GetFileNameWithoutExtention(const String& path) +{ + int a = path.find_last_of(_text("/")); + int b = path.find_last_of(_text(".")); + if(a == -1 && b == -1) + return path; + if(b == -1) + { + int length = path.length(); + return path.substr(a+1, length - a); + } + return path.substr(a+1, b-a - 1); +} + +extern void GetRootFolderS(const String& path, String& folderName, String& resultPath) +{ + int start = path.find_first_not_of(_text("/")); + + int offset = path.find_first_of(_text("/"),start); + if(start < 0) + { + folderName = path; + resultPath = String(_text("")); + return; + } + if(offset < 0) + { + folderName = path.substr(start, path.length()); + resultPath = String(_text("")); + return; + } + folderName = path.substr(start, offset - start); + resultPath = path.substr(offset, path.length() - offset); +} + +extern void GetRootFolderS(const String& path, String& folderName) +{ + int start = path.find_first_not_of(_text("/")); + int offset = path.find_first_of(_text("/"),start); + folderName = path.substr(start, offset - start); +} + +extern String GetDirectoryName(const String& path) +{ + String ret = path; + if(ret.length()<2) return ret; + while(ret.length() > 1 && (ret.c_str()[ret.length()-1]) == L'\\') + ret = ret.substr(0,ret.length()-1); + + int a = ret.find_last_of(PATH_SPLITTER); + if(a == -1) + return ret; + int length = ret.length(); + return ret.substr(a+1, length - a); +} + +extern void SplitString(const String& text, std::vector* collection) +{ + int pos = 0; + int counter = 0; + const char* str = text.c_str(); + while(str[counter]) + { + if(str[counter] == '\n') + { + (*collection).push_back(text.substr(pos, counter - pos)); + pos = counter + 1; + } + counter++; + } + int diff = text.size() - pos; + if(diff) + (*collection).push_back(text.substr(pos, diff)); +} + +extern void SplitString(const String& text, std::vector* collection, char splitter) +{ + int pos = 0; + int counter = 0; + const char* str = text.c_str(); + while(str[counter]) + { + if(str[counter] == splitter) + { + (*collection).push_back(text.substr(pos, counter - pos)); + pos = ++counter; + //counter++; + } + counter++; + } + int diff = text.size() - pos; + if(diff) + (*collection).push_back(text.substr(pos, diff)); +} + +extern void SplitStringW(const std::wstring& text, std::vector* collection) +{ + int pos = 0; + int counter = 0; + const wchar_t* str = text.c_str(); + while(str[counter]) + { + if(str[counter] == L'\n') + { + (*collection).push_back(text.substr(pos, counter - pos)); + pos = counter + 1; + } + counter++; + } + int diff = text.size() - pos; + if(diff) + (*collection).push_back(text.substr(pos, diff)); +} + +extern void SplitStringW(const std::wstring& text, std::vector* collection, wchar_t splitter) +{ + int pos = 0; + int counter = 0; + const wchar_t* str = text.c_str(); + while(str[counter]) + { + if(str[counter] == splitter) + { + (*collection).push_back(text.substr(pos, counter - pos)); + pos = counter; + } + counter++; + } + int diff = text.size() - pos; + if(diff) + (*collection).push_back(text.substr(pos, diff)); +} + + +extern void StringSerialize(const String& src, LifeCore::IMemoryStream* stm) +{ + //HACK Multi byte string may be not only one byte per character + String::size_type size = src.length(); + stm->Write(size); + stm->Write(src.c_str(), size); +} +extern String StringDeserialize(LifeCore::IMemoryStream* stm) +{ + String::size_type size; + stm->Read(&size, sizeof(size)); + char* buffer = new char[size + 1]; + buffer[size] = 0; + stm->Read(buffer, size); + String val = String(buffer); + delete[] buffer; + return val; +} + +extern void WCSTMBS(const wchar_t* src, String& dst) +{ + int length = wcslen(src); + int len = WideCharToMultiByte(CP_ACP, 0, src, length, 0, 0,0,0); + char* buf = new char[len+1]; + memset(buf,0,len+1); + WideCharToMultiByte(CP_ACP, 0, src, length, buf, len+1,0,0); + dst = String(buf); + delete[] buf; +} + +extern void MBSTWCS(const String& src, std::wstring& dst) +{ + int strLen = MultiByteToWideChar(CP_ACP, 0, src.c_str(), -1, NULL, 0); + wchar_t* widePath = new wchar_t[strLen]; + MultiByteToWideChar(CP_ACP, 0, src.c_str(), -1, widePath, strLen); + dst = std::wstring(widePath); + delete[]widePath; +} \ No newline at end of file diff --git a/LifeCore/LString.h b/LifeCore/LString.h new file mode 100644 index 0000000..b184ef5 --- /dev/null +++ b/LifeCore/LString.h @@ -0,0 +1,75 @@ +#ifndef LSTRING_H__ +#define LSTRING_H__ + +#define _SCL_SECURE_NO_WARNINGS +#include + +#include + +#include +#include "IMemoryStream.h" + + + +#ifdef _UNICODE +typedef std::wstring String; +#define _text(str) L##str + +#define PRINT wprintf +#define STRCMP wcscmp +typedef wchar_t tchar; +#define string_size(str) (str.length() * 2) + +#else + +#define _text(str) str +#define PRINT printf +typedef std::string String; +typedef char tchar; +#define STRCMP strcmp +#define string_size(str) (str.length()) +#endif + +#ifdef WIN32 +#define PATH_SPLITTER _text("\\") +#else +#define PATH_SPLITTER _text("/") +#endif + +typedef std::vector StringVector; +typedef std::vector WStringVector; + +extern int StrToInt(const String& str); + +extern String GetFileName(const String& path); + +extern String GetFileName(const String& path, const String& pathSplitter); + +extern String GetFileNameWithoutExtention(const String& path); + +extern String GetFilePath(const String& path, const String& pathSplitter); + +extern void GetRootFolderS(const String& path, String& folderName, String& resultPath); + +extern void GetRootFolderS(const String& path, String& folderName); + +extern String GetDirectoryName(const String& path); + +extern void SplitString(const String& text, std::vector* collection); + +extern void SplitString(const String& text, std::vector* collection, char splitter); + +extern void SplitStringW(const std::wstring& text, std::vector* collection); + +extern void SplitStringW(const std::wstring& text, std::vector* collection, wchar_t splitter); + +extern void WCSTMBS(const wchar_t* src, String& dst); +extern void MBSTWCS(const String& src, std::wstring& dst); + +extern void StringSerialize(const String& src, LifeCore::IMemoryStream* stm); +extern String StringDeserialize(LifeCore::IMemoryStream* stm); + +#define AS_STRING(omg) String(_text(##omg##)) +#define STR(omg) String(_text(##omg##)) + +#endif \ No newline at end of file diff --git a/LifeCore/LifeCore.vcxproj b/LifeCore/LifeCore.vcxproj new file mode 100644 index 0000000..7fb453a --- /dev/null +++ b/LifeCore/LifeCore.vcxproj @@ -0,0 +1,299 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {E83FE0FE-36DC-4F92-B074-5A7280FE6C17} + Win32Proj + LifeCore + + + + StaticLibrary + true + v120 + MultiByte + + + StaticLibrary + true + v120 + MultiByte + + + StaticLibrary + false + v120 + true + MultiByte + + + StaticLibrary + false + v120 + true + MultiByte + + + + + + + + + + + + + + + + + + + $(SolutionDir)\bin\ + $(SolutionDir)\tmp\$(Configuration)\ + $(ProjectName)_d + + + $(ProjectName)_d + $(SolutionDir)$(ProjectName)\lib\$(Platform)\ + $(SolutionDir)$(ProjectName)\tmp\$(Platform)\$(Configuration)\ + + + $(SolutionDir)\bin\ + $(SolutionDir)\tmp\$(Configuration)\ + + + $(SolutionDir)\bin\ + $(SolutionDir)\tmp\$(Configuration)\ + + + + + + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + C:\nettlemath\nettlemath;$(REPO_DIR)/ois-v1-3/includes;$(REPO_DIR)/zlib-1.2.5/ + true + false + + + Windows + true + + + + + + + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + $(REPO_DIR)/ois-v1-3/includes;$(REPO_DIR)/zlib-1.2.5 + + + Windows + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + C:\nettlemath\nettlemath;$(REPO_DIR)/ois-v1-3/includes;$(REPO_DIR)/zlib-1.2.5/ + + + Windows + true + true + true + + + OIS_static.lib + $(REPO_DIR)\ois-v1-3\lib\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + $(REPO_DIR)/ois-v1-3/includes + + + Windows + true + true + true + + + + + + \ No newline at end of file diff --git a/LifeCore/PixelBuffer.cpp b/LifeCore/PixelBuffer.cpp new file mode 100644 index 0000000..987ece7 --- /dev/null +++ b/LifeCore/PixelBuffer.cpp @@ -0,0 +1,10 @@ +/* +#include "PixelBuffer.h" + +namespace LifeCore { + PixelBuffer::PixelBuffer(uint32 _format, uint32 _width, uint32 _height, uint8 _bpp, uint32 _stride) + :format(format), width(_width), height(_height), bpp(_bpp), stride(_stride), RawBuffer(_stride * _height) + { + + } +}*/ \ No newline at end of file diff --git a/LifeCore/PixelBuffer.h b/LifeCore/PixelBuffer.h new file mode 100644 index 0000000..fb2fe87 --- /dev/null +++ b/LifeCore/PixelBuffer.h @@ -0,0 +1,21 @@ +/* +#ifndef PixelBuffer_h__ +#define PixelBuffer_h__ + +#include "RawBuffer.h" +#include "Types.h" + +namespace LifeCore { + class PixelBuffer : public RawBuffer { + public: + PixelBuffer(uint32 _format, uint32 _width, uint32 _height, uint8 _bpp, uint32 _stride); + private: + uint32 format; + uint32 width; + uint32 height; + uint8 bpp; + uint32 stride; + }; +} +#endif // PixelBuffer_h__ +*/ diff --git a/LifeCore/Property.h b/LifeCore/Property.h new file mode 100644 index 0000000..f7392c4 --- /dev/null +++ b/LifeCore/Property.h @@ -0,0 +1,183 @@ +#ifndef Property_h__ +#define Property_h__ + +#include "delegate/Delegate.h" +#include "IProperty.h" +#include "Utils.h" +#include + +namespace LifeCore +{ +#define DEFINE_PROPERTY(___name, ___type) \ + Core::Property<##___type##>*## ___name; + +#define ALLOC_PROPERTY(_name, _type) \ + new Core::Property<##_type##>(STR(#_name)); + + template + class Property : public IProperty + { + public: + Property() + { + } + void Set(const void* src, size_t size){ + //size must be equal + assert(sizeof(T) == size); + memcpy(&value, src, size); + } + const void* GetPtr(){ + return &value; + } + bool Serialize(LifeCore::IMemoryStream* stm) + { + StringSerialize(name, stm); + stm->Write(size_t(sizeof(value))); + stm->Write(&value, sizeof(value)); + return true; + } + bool Deserialize(LifeCore::IMemoryStream* stm) + { + name = StringDeserialize(stm); + size_t data_len; + stm->Read(&data_len, sizeof(data_len)); + stm->Read(&value, data_len); + return true; + } + const Property& operator=(const T& other) + { + value = other; + OnValueChanged(); + return *this; + } + const Property& operator=(const Property& other) + { + value = other.value; + OnValueChanged(); + return *this; + } + const T& operator()() + { + return value; + } + String ToString() { return STR(typeid(T).name()); }; + void FromString(const String& str){} + const String& GetName() + { + return name; + } + private: + String name; + + T value; + }; + + bool Property::Serialize(LifeCore::IMemoryStream* stm) + { + StringSerialize(name, stm); + StringSerialize(value, stm); + return true; + } + bool Property::Deserialize(LifeCore::IMemoryStream* stm) + { + name = StringDeserialize(stm); + value = StringDeserialize(stm); + return true; + } + + + void Property::FromString(const String& str) + { + *this = str; + } + void Property::FromString(const String& str) + { + *this = StringToInt8(str); + } + void Property::FromString(const String& str) + { + *this = StringToInt16(str); + } + void Property::FromString(const String& str) + { + *this = StringToInt32(str); + } + void Property::FromString(const String& str) + { + *this = StringToInt64(str); + } + void Property::FromString(const String& str) + { + *this = StringToUInt8(str); + } + void Property::FromString(const String& str) + { + *this = StringToUInt16(str); + } + void Property::FromString(const String& str) + { + *this = StringToUInt32(str); + } + void Property::FromString(const String& str) + { + *this = StringToUInt64(str); + } + void Property::FromString(const String& str) + { + *this = StringToFloat(str); + } + void Property::FromString(const String& str) + { + *this = StringToDouble(str); + } + String Property::ToString() + { + return value; + } + String Property::ToString() + { + return Int8ToString(value); + } + String Property::ToString() + { + return Int16ToString(value); + } + String Property::ToString() + { + return Int32ToString(value); + } + String Property::ToString() + { + return Int64ToString(value); + } + String Property::ToString() + { + return UInt8ToString(value); + } + String Property::ToString() + { + return UInt16ToString(value); + } + String Property::ToString() + { + return UInt32ToString(value); + } + String Property::ToString() + { + return UInt64ToString(value); + } + String Property::ToString() + { + return BoolToString(value); + } + String Property::ToString() + { + return FloatToString(value); + } + String Property::ToString() + { + return DoubleToString(value); + } +} + +#endif // Property_h__ \ No newline at end of file diff --git a/LifeCore/Range.h b/LifeCore/Range.h new file mode 100644 index 0000000..12b6b7b --- /dev/null +++ b/LifeCore/Range.h @@ -0,0 +1,20 @@ +#ifndef RANGE_H__ +#define RANGE_H__ + +namespace LifeCore +{ + template + class Range + { + public: + T RangeStart; + T RangeEnd; + Range(T _start, T _end) + { + RangeStart = _start; + RangeEnd = _end; + } + }; +} + +#endif \ No newline at end of file diff --git a/LifeCore/RawBuffer.h b/LifeCore/RawBuffer.h new file mode 100644 index 0000000..8f629e4 --- /dev/null +++ b/LifeCore/RawBuffer.h @@ -0,0 +1,30 @@ +#ifndef RawBuffer_h__ +#define RawBuffer_h__ + +#include +#include "media/WinMfMediaType.h" + +namespace LifeCore { + template + class RawBuffer { + public: + RawBuffer(size_t size, WinMfMediaType _type) + :dataSize(size), Type(_type) + { + data = new T[size]; + } + void SetData(void* pData) { + memcpy_s(data, dataSize, pData, dataSize); + } + virtual ~RawBuffer() { + delete[] data; + } + WinMfMediaType Type; + T *data; + size_t dataSize; + protected: + + + }; +} +#endif // RawBuffer_h__ diff --git a/LifeCore/Singleton.h b/LifeCore/Singleton.h new file mode 100644 index 0000000..ec7fe07 --- /dev/null +++ b/LifeCore/Singleton.h @@ -0,0 +1,27 @@ +#ifndef ___SINGLETON_H__ +#define ___SINGLETON_H__ + +#include + +template +class Singleton +{ +private: + static std::auto_ptr instance; + + Singleton(const Singleton& rval){} +public: + Singleton(){} + virtual ~Singleton(){} + static T *me() + { + if (!instance.get()) + instance.reset(new T()); + return instance.get(); + } +}; + +template +std::auto_ptr Singleton::instance; + +#endif // ___SINGLETON_H__ \ No newline at end of file diff --git a/LifeCore/Timer.cpp b/LifeCore/Timer.cpp new file mode 100644 index 0000000..2de8c9b --- /dev/null +++ b/LifeCore/Timer.cpp @@ -0,0 +1,60 @@ +#include "Timer.h" +#include + +//#define TIMER_TGT + +namespace LifeCore +{ + Timer::Timer() + :timeElapsed(0) + { +#ifdef TIMER_TGT + if(refCount == 0) + timeBeginPeriod(1); + refCount++; +#else + QueryPerformanceFrequency((LARGE_INTEGER *)&perfFreq); +#endif + } + Timer::~Timer() + { +#ifdef TIMER_TGT + refCount--; + if(refCount == 0) + timeEndPeriod(1); +#endif + } + void Timer::Start() + { +#ifdef TIMER_TGT + lastTime = timeGetTime(); +#else + DWORD_PTR old_mask = SetThreadAffinityMask(GetCurrentThread(), 0); + QueryPerformanceCounter((LARGE_INTEGER *)&lastTime); + SetThreadAffinityMask(GetCurrentThread(), old_mask); +#endif + } + void Timer::Stop() + { +#ifdef TIMER_TGT + curTime = timeGetTime(); + diff = curTime - lastTime; + timeElapsed = 1.0 / (double)diff; //time in SECONDS +#else + DWORD_PTR old_mask = SetThreadAffinityMask(GetCurrentThread(), 0); + QueryPerformanceCounter((LARGE_INTEGER *)&curTime); + SetThreadAffinityMask(GetCurrentThread(), old_mask); + if(lastTime == 0) + timeElapsed = 0; + else + { + timeElapsed = (float)((double)(curTime - lastTime) / (double)perfFreq); + } + +#endif + } + float Timer::getTimeElapsed() + { + return timeElapsed; + } +} \ No newline at end of file diff --git a/LifeCore/Timer.h b/LifeCore/Timer.h new file mode 100644 index 0000000..4874dc9 --- /dev/null +++ b/LifeCore/Timer.h @@ -0,0 +1,31 @@ +#ifndef Timer_h__ +#define Timer_h__ + +#define TIMER_QPC + +#ifndef TIMER_QPC +#define TIMER_TGT +#define TIMER_TYPES DWORD +#else +#define TIMER_TYPES __int64 +#endif + +namespace LifeCore +{ + static int refCount = 0; + + class Timer + { + private: + TIMER_TYPES lastTime, curTime, diff, perfFreq; + float timeElapsed; + public: + Timer(); + ~Timer(); + void Start(); + void Stop(); + float getTimeElapsed(); + }; +} + +#endif // Timer_h__ \ No newline at end of file diff --git a/LifeCore/Types.h b/LifeCore/Types.h new file mode 100644 index 0000000..305d439 --- /dev/null +++ b/LifeCore/Types.h @@ -0,0 +1,36 @@ +#ifndef Types_h__ +#define Types_h__ + +#include "LString.h" + +typedef unsigned char _bool; +typedef unsigned char byte; + +#undef INLINE +#ifdef _WIN32 +#ifdef _MSC_VER +#define INLINE __forceinline +#endif +#else +#ifdef __GNUC__ +#define INLINE __attribute__((always_inline)) +#endif +#endif + +#ifndef INLINE +#define INLINE inline +#endif + +typedef unsigned long long uint64; +typedef unsigned int uint32; +typedef unsigned short uint16; +typedef unsigned char uint8; + +typedef long long int64; +typedef int int32; +typedef short int16; +typedef char int8; + +typedef float real; + +#endif // Types_h__ \ No newline at end of file diff --git a/LifeCore/Utils.cpp b/LifeCore/Utils.cpp new file mode 100644 index 0000000..3eb04c2 --- /dev/null +++ b/LifeCore/Utils.cpp @@ -0,0 +1,221 @@ +#include "Utils.h" +#include + +extern int8 StringToInt8(const String& str){ + int32 val2; + std::stringstream ss; + ss << str; + ss >> val2; + return (int8)val2; +} + +extern uint8 StringToUInt8(const String& str) +{ + uint32 val2; + //sscanf(str.c_str(), "%hu", &val2); + std::stringstream ss; + ss << str; + ss >> val2; + return (uint8)val2; +} + +extern int16 StringToInt16(const String& str) +{ + int16 val2; + //sscanf(str.c_str(), "%hi", &val2); + std::stringstream ss; + ss << str; + ss >> val2; + return val2; +} + +extern uint16 StringToUInt16(const String& str) +{ + uint16 val2; + //sscanf(str.c_str(), "%hu", &val2); + std::stringstream ss; + ss << str; + ss >> val2; + return val2; +} + +extern int32 StringToInt32(const String& str) +{ + int32 val2; + //sscanf(str.c_str(), "%i", &val2); + std::stringstream ss; + ss << str; + ss >> val2; + return val2; +} + +extern uint32 StringToUInt32(const String& str) +{ + uint32 val2; + //sscanf(str.c_str(), "%u", &val2); + std::stringstream ss; + ss << str; + ss >> val2; + return val2; +} + +extern int64 StringToInt64(const String& str) +{ + int64 val2; + //sscanf(str.c_str(), "%l", &val2); + std::stringstream ss; + ss << str; + ss >> val2; + return val2; +} + +extern uint64 StringToUInt64(const String& str) +{ + uint64 val2; + //sscanf(str.c_str(), "%ul", &val2); + std::stringstream ss; + ss << str; + ss >> val2; + return val2; +} + +extern float StringToFloat(const String& str) +{ + float val2; + //sscanf(str.c_str(), "%f", &val); + std::stringstream ss; + ss << str; + ss >> val2; + return val2; +} + +extern double StringToDouble(const String& str) +{ + float val2; + //sscanf(str.c_str(), "%d", &val); + std::stringstream ss; + ss << str; + ss >> val2; + return val2; +} + +extern String Int8ToString(int8 value) +{ + /*char buffer[16]; + memset(buffer,0,16); + sprintf(buffer, "%hi", value);*/ + std::stringstream ss; + ss << value; + return ss.str(); +} + +extern String Int16ToString(int16 value) +{ + /*char buffer[16]; + memset(buffer,0,16); + sprintf(buffer, "%hi", value); + return String(buffer);*/ + std::stringstream ss; + ss << value; + return ss.str(); +} + +extern String Int32ToString(int32 value) +{ + /*char buffer[16]; + memset(buffer,0,16); + sprintf(buffer, "%i", value); + return String(buffer);*/ + std::stringstream ss; + ss << value; + return ss.str(); +} + +extern String Int64ToString(int64 value) +{ + /*char buffer[32]; + memset(buffer,0,32); + sprintf(buffer, "%li", value); + return String(buffer);*/ + std::stringstream ss; + ss << value; + return ss.str(); +} + +extern String UInt8ToString(uint8 value) +{ + /*char buffer[16]; + memset(buffer,0,16); + sprintf(buffer, "%hu", value); + return String(buffer);*/ + std::stringstream ss; + ss << value; + return ss.str(); +} + +extern String UInt16ToString(uint16 value) +{ + /*char buffer[16]; + memset(buffer,0,16); + sprintf(buffer, "%hu", value); + return String(buffer);*/ + std::stringstream ss; + ss << value; + return ss.str(); +} + +extern String UInt32ToString(uint32 value) +{ + /*char buffer[16]; + memset(buffer,0,16); + sprintf(buffer, "%u", value); + return String(buffer);*/ + std::stringstream ss; + ss << value; + return ss.str(); +} + +extern String UInt64ToString(uint32 value) +{ + /*char buffer[32]; + memset(buffer,0,32); + sprintf(buffer, "%lu", value); + return String(buffer);*/ + std::stringstream ss; + ss << value; + return ss.str(); +} + +extern String FloatToString(float val) +{ + /*char buffer[32]; + memset(buffer,0,32); + sprintf(buffer, "%f", val); + return String(buffer);*/ + std::stringstream ss; + ss << val; + return ss.str(); +} + +extern String DoubleToString(double val) +{ + /*char buffer[64]; + memset(buffer,0,64); + sprintf(buffer, "%d", val); + return String(buffer);*/ + std::stringstream ss; + ss << val; + return ss.str(); +} + +extern String BoolToString(bool value) +{ + return value == true ? STR("true") : STR("false"); +} + +extern bool StringToBool(const String& str) +{ + String strCopy = str; + std::transform(strCopy.begin(), strCopy.end(), strCopy.begin(), ::tolower); + return str == STR("true") ? true : false; +} \ No newline at end of file diff --git a/LifeCore/Utils.h b/LifeCore/Utils.h new file mode 100644 index 0000000..db5f59d --- /dev/null +++ b/LifeCore/Utils.h @@ -0,0 +1,63 @@ +#ifndef Utils_h__ +#define Utils_h__ + +#include "Types.h" +#include "LString.h" +#include +#include +#include + +//TODO fix me, plz, cuz I'm just copy of int16 conversion! +extern int8 StringToInt8(const String& str); + +extern uint8 StringToUInt8(const String& str); + +extern int16 StringToInt16(const String& str); + +extern uint16 StringToUInt16(const String& str); + +extern int32 StringToInt32(const String& str); + +extern uint32 StringToUInt32(const String& str); + +extern int64 StringToInt64(const String& str); + +extern uint64 StringToUInt64(const String& str); + +extern float StringToFloat(const String& str); + +extern double StringToDouble(const String& str); + +extern String Int8ToString(int8 value); + +extern String Int16ToString(int16 value); + +extern String Int32ToString(int32 value); + +extern String Int64ToString(int32 value); + +extern String UInt8ToString(uint8 value); + +extern String UInt16ToString(uint16 value); + +extern String UInt32ToString(uint32 value); + +extern String UInt64ToString(uint32 value); + +extern String FloatToString(float val); + +extern String DoubleToString(double val); + +extern String BoolToString(bool value); + +extern bool StringToBool(const String& str); + + +template void SafeRelease(T **ppT) { + if(*ppT) { + (*ppT)->Release(); + *ppT = NULL; + } +} + +#endif // Utils_h__ \ No newline at end of file diff --git a/LifeCore/Window.h b/LifeCore/Window.h new file mode 100644 index 0000000..d1614a7 --- /dev/null +++ b/LifeCore/Window.h @@ -0,0 +1,36 @@ +#ifndef Window_h__ +#define Window_h__ + +#include "IWindow.h" +#include "delegate/Delegate.h" +#include "Build.h" +#include "LString.h" +#include "DisplayMode.h" +#include "IWindowDemander.h" +#include "IInputSystem.h" + +namespace LifeCore { + class Window : public IWindow { + public: + //events + EVENT(ExitEvent); + ExitEvent Event_Quit; + + EVENT1(WindowEvent, IWindow*); + WindowEvent Event_Resize; + WindowEvent Event_SwitchFullscreen; + WindowEvent Event_Deactivate; + WindowEvent Event_Activate; + + protected: + bool bCursorVisible; + bool bModeSwitching; + bool bFullscreen; + bool bActive; + int width; + int height; + String caption; + DisplayMode fsMode; + }; +} +#endif // Window_h__ diff --git a/LifeCore/WindowPixelManager.cpp b/LifeCore/WindowPixelManager.cpp new file mode 100644 index 0000000..bc9f5e0 --- /dev/null +++ b/LifeCore/WindowPixelManager.cpp @@ -0,0 +1,34 @@ +#include "WindowPixelManager.h" + +namespace LifeCore { + WindowPixelManager::WindowPixelManager(WindowWin* wnd, int bpp) + :window(wnd) + { + hwnd = (HWND)(window->GetWindowHandle()); + + ::ZeroMemory(&bitmapInfo, sizeof(BITMAPINFOHEADER)); + bitmapInfo.biSize = sizeof(BITMAPINFOHEADER); + bitmapInfo.biWidth = wnd->GetWidth(); + bitmapInfo.biHeight = -wnd->GetHeight(); + bitmapInfo.biPlanes = 1; + bitmapInfo.biBitCount = bpp; + bitmapInfo.biCompression = 0; + bitmapInfo.biSizeImage = wnd->GetWidth() * wnd->GetHeight() * (bpp / 8); + + info.bmiHeader = bitmapInfo; + + context = ::GetDC(hwnd); + } + + void WindowPixelManager::SetPixels(int width, int height, void* pixels) { + + bitmapInfo.biWidth = width; + bitmapInfo.biHeight = -height; + bitmapInfo.biSizeImage = width * height * (bitmapInfo.biBitCount / 8); + info.bmiHeader = bitmapInfo; + + SetDIBitsToDevice(context, 0, 0, width, height, 0, 0, 0, height, pixels, &info, 0); + } + + /**/ +} \ No newline at end of file diff --git a/LifeCore/WindowPixelManager.h b/LifeCore/WindowPixelManager.h new file mode 100644 index 0000000..32fb840 --- /dev/null +++ b/LifeCore/WindowPixelManager.h @@ -0,0 +1,20 @@ +#ifndef WINDOW_PIXEL_MANAGER_H__ +#define WINDOW_PIXEL_MANAGER_H__ + +#include "WindowWin.h" +#include + +namespace LifeCore { + class WindowPixelManager { + public: + WindowPixelManager(WindowWin* wnd, int bpp); + void SetPixels(int width, int height, void* pixels); + private: + WindowWin* window; + BITMAPINFOHEADER bitmapInfo; + BITMAPINFO info; + HWND hwnd; + HDC context; + }; +} +#endif \ No newline at end of file diff --git a/LifeCore/WindowWin.cpp b/LifeCore/WindowWin.cpp new file mode 100644 index 0000000..920685f --- /dev/null +++ b/LifeCore/WindowWin.cpp @@ -0,0 +1,548 @@ +#include "WindowWin.h" +#define L_WIN32 +#include +#include "log/NLog.h" + +namespace LifeCore { + LRESULT __stdcall WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); + + WindowWin::WindowWin() { + bFullscreen = false; + bActive = true; + bModeSwitching = false; + bCursorVisible = true; + bAltEnterLocked = false; + + width = 256; + height = 256; + + g_hInst = GetModuleHandle(NULL); + g_hwnd = NULL; + + //memset(&g_wndClass,0,sizeof(WNDCLASSEX)); + + WNDCLASSEX g_wndClass = { NULL }; + + g_wndClass.cbSize = sizeof(WNDCLASSEX); + g_wndClass.cbClsExtra = NULL; + g_wndClass.cbWndExtra = NULL; + g_wndClass.hbrBackground = (HBRUSH)GetStockObject(1); + g_wndClass.hCursor = (HCURSOR)LoadCursor(NULL, IDC_ARROW); + g_wndClass.hIcon = LoadIcon(g_hInst, IDI_APPLICATION); + g_wndClass.hIconSm = LoadIcon(g_hInst, IDI_APPLICATION); + g_wndClass.hInstance = g_hInst; + g_wndClass.lpfnWndProc = WndProc; + g_wndClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + g_wndClass.lpszMenuName = NULL; + g_wndClass.lpszClassName = _text("Onotole"); + + if(!RegisterClassEx(&g_wndClass)) { + MessageBox(g_hwnd, _text("Can not create window class! We all gonna die!!"), _text("OH SHIT!"), MB_OK); + } + + //calculate window size + RECT NewWindowSize; + NewWindowSize.top = 30; + NewWindowSize.left = 300; + NewWindowSize.bottom = NewWindowSize.top + height; + NewWindowSize.right = NewWindowSize.left + width; + + AdjustWindowRect(&NewWindowSize, WS_OVERLAPPEDWINDOW, false); + + /*WS_MINIMIZEBOX + WS_MAXIMIZEBOX*/ + + if(!(g_hwnd = CreateWindowEx(NULL, _text("Onotole"), _text("LifeEngine render window"), WS_THICKFRAME, + NewWindowSize.left, NewWindowSize.top, NewWindowSize.right - NewWindowSize.left, NewWindowSize.bottom - NewWindowSize.top, NULL, NULL, g_hInst, NULL))) { + MessageBox(g_hwnd, _text("Can not create window! We all gonna die!!"), _text("HOLY SHIT!"), MB_OK); + } + + ShowWindow(g_hwnd, SW_SHOWNORMAL); + SetForegroundWindow(g_hwnd); + SetFocus(g_hwnd); + + ZeroMemory(&mode, sizeof(DEVMODE)); + mode.dmSize = sizeof(DEVMODE); + EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &mode); + + WindowWin* ptr = this; + long val = (long)ptr; + SetWindowLongPtr(g_hwnd, GWLP_USERDATA, val); + + fsMode.width = 640; + fsMode.height = 480; + fsMode.refreshRate = 60; + fsMode.bpp = 4; + + CenterWindow(); + } + + bool WindowWin::IsKeyPressed(KeyCodes::Key key) { + return false; + } + + void WindowWin::Update() { +#ifdef L_WIN32 + MSG msg = { 0 }; + while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { + switch(msg.message) { + case WM_QUIT: + { + Event_Quit.Invoke(); + //exit(0); + } + break; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } +#endif + } + void WindowWin::SetCaption(const String& val) { + caption = val; + SetWindowText(g_hwnd, caption.c_str()); + } + void WindowWin::SetFullScreen(bool state) { + bFullscreen = state; + } + bool WindowWin::IsFullScreen() { + return bFullscreen; + } + WindowWin::~WindowWin(void) { + ChangeDisplaySettings(&mode, 0); + } + + void WindowWin::OnActivate() { + bActive = true; + Event_Activate.Invoke(this); + } + void WindowWin::OnDeactivate() { + bActive = false; + Event_Deactivate.Invoke(this); + } + void WindowWin::OnResize() { + if(!IsFullScreen() || !bModeSwitching) { + RECT r; + ::GetClientRect(g_hwnd, &r); + width = r.right - r.left; + height = r.bottom - r.top; + } + Event_Resize.Invoke(this); + } + bool WindowWin::IsActive() { + return bActive; + } + bool WindowWin::IsModeSwitching() { + return bModeSwitching; + } + void WindowWin::SetCursorVisible(bool state) { + bCursorVisible = state; + } + bool WindowWin::IsCursorVisible() { + return bCursorVisible; + } + + bool WindowWin::SwitchMode(bool fullscreen) { + bFullscreen = fullscreen; + + if(bFullscreen) { + bModeSwitching = true; + DEVMODE fmode; + ZeroMemory(&fmode, sizeof(DEVMODE)); + fmode.dmSize = sizeof(DEVMODE); + fmode.dmPelsWidth = fsMode.width; + fmode.dmPelsHeight = fsMode.height; + fmode.dmBitsPerPel = mode.dmBitsPerPel; + fmode.dmDisplayFrequency = fsMode.refreshRate; + fmode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY; + + //if(ChangeDisplaySettings(&fmode,0)!= DISP_CHANGE_SUCCESSFUL) + // return false; + + SetWindowLong(g_hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW | WS_EX_TOPMOST); + SetWindowLong(g_hwnd, GWL_STYLE, WS_POPUP); + SetWindowPos(g_hwnd, HWND_TOPMOST, 0, 0, fsMode.width, fsMode.height, SWP_SHOWWINDOW | SWP_FRAMECHANGED); + bModeSwitching = false; + } else { + //if(ChangeDisplaySettings(&mode_window,0)!= DISP_CHANGE_SUCCESSFUL) + // return false; + SetWindowLong(g_hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW); + SetWindowLong(g_hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW); + SetSize(width, height); + CenterWindow(); + } + Event_SwitchFullscreen.Invoke(this); + return true; + } + void WindowWin::CenterWindow() { + int mx = GetSystemMetrics(SM_CXSCREEN); + int my = GetSystemMetrics(SM_CYSCREEN); + + int nx = (mx - width) / 2; + int ny = (my - height) / 2; + + SetWindowPos(g_hwnd, NULL, nx, ny, -1, -1, + SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + } + void WindowWin::SetFullscreenMode(LifeCore::DisplayMode mode) { + fsMode = mode; + } + void WindowWin::SetSize(int w, int h) { + if(!IsFullScreen()) { + width = w; + height = h; + RECT NewWindowSize; + NewWindowSize.top = 50; + NewWindowSize.left = 50; + NewWindowSize.bottom = NewWindowSize.top + h; + NewWindowSize.right = NewWindowSize.left + w; + + AdjustWindowRect(&NewWindowSize, WS_OVERLAPPEDWINDOW, false); + SetWindowPos(g_hwnd, HWND_TOP, NewWindowSize.left, NewWindowSize.top, NewWindowSize.right - NewWindowSize.left, NewWindowSize.bottom - NewWindowSize.top, SWP_SHOWWINDOW | SWP_FRAMECHANGED); + //::MoveWindow(g_hwnd,NewWindowSize.left, NewWindowSize.top, NewWindowSize.right - NewWindowSize.left, NewWindowSize.bottom - NewWindowSize.top, true); + CenterWindow(); + } + } + + KeyCodes::Key WindowWin::ConvertKeyCode(unsigned long key_code) { + switch(key_code) { + case VK_ESCAPE: + return KeyCodes::KEY_ESCAPE; + case VK_F1: + return KeyCodes::KEY_F1; + case VK_F2: + return KeyCodes::KEY_F2; + case VK_F3: + return KeyCodes::KEY_F3; + case VK_F4: + return KeyCodes::KEY_F4; + case VK_F5: + return KeyCodes::KEY_F5; + case VK_F6: + return KeyCodes::KEY_F6; + case VK_F7: + return KeyCodes::KEY_F7; + case VK_F8: + return KeyCodes::KEY_F8; + case VK_F9: + return KeyCodes::KEY_F9; + case VK_F10: + return KeyCodes::KEY_F10; + case VK_F11: + return KeyCodes::KEY_F11; + case VK_F12: + return KeyCodes::KEY_F12; + case VK_SNAPSHOT: + return KeyCodes::KEY_PRINT_SCREEN; + case VK_SCROLL: + return KeyCodes::KEY_SCROLL_LOCK; + case VK_PAUSE: + return KeyCodes::KEY_PAUSE; + + case VK_OEM_3: + return KeyCodes::KEY_TILDE; + case 0x30: + return KeyCodes::KEY_1; + case 0x31: + return KeyCodes::KEY_2; + case 0x32: + return KeyCodes::KEY_3; + case 0x33: + return KeyCodes::KEY_4; + case 0x34: + return KeyCodes::KEY_5; + case 0x35: + return KeyCodes::KEY_6; + case 0x36: + return KeyCodes::KEY_7; + case 0x37: + return KeyCodes::KEY_8; + case 0x38: + return KeyCodes::KEY_9; + case 0x39: + return KeyCodes::KEY_0; + case 0xBD: + return KeyCodes::KEY_MINUS; + case 0xBB: + return KeyCodes::KEY_EQUAL; + case VK_BACK: + return KeyCodes::KEY_BACKSPACE; + + case VK_TAB: + return KeyCodes::KEY_TAB; + case VK_CAPITAL: + return KeyCodes::KEY_CAPS_LOCK; + case VK_SHIFT: + return KeyCodes::KEY_SHIFT; + case VK_CONTROL: + return KeyCodes::KEY_CONTROL; + case VK_LWIN: + return KeyCodes::KEY_META_LEFT; + case VK_RWIN: + return KeyCodes::KEY_META_RIGHT; + case VK_MENU: + return KeyCodes::KEY_ALT; + case VK_SPACE: + return KeyCodes::KEY_SPACE; + case VK_APPS: + return KeyCodes::KEY_MENU; + case VK_RETURN: + return KeyCodes::KEY_ENTER; + + case 0x41: + return KeyCodes::KEY_A; + case 0x42: + return KeyCodes::KEY_B; + case 0x43: + return KeyCodes::KEY_C; + case 0x44: + return KeyCodes::KEY_D; + case 0x45: + return KeyCodes::KEY_E; + case 0x46: + return KeyCodes::KEY_F; + case 0x47: + return KeyCodes::KEY_G; + case 0x48: + return KeyCodes::KEY_H; + case 0x49: + return KeyCodes::KEY_I; + case 0x4A: + return KeyCodes::KEY_J; + case 0x4B: + return KeyCodes::KEY_K; + case 0x4C: + return KeyCodes::KEY_L; + case 0x4D: + return KeyCodes::KEY_M; + case 0x4E: + return KeyCodes::KEY_N; + case 0x4F: + return KeyCodes::KEY_O; + case 0x50: + return KeyCodes::KEY_P; + case 0x51: + return KeyCodes::KEY_Q; + case 0x52: + return KeyCodes::KEY_R; + case 0x53: + return KeyCodes::KEY_S; + case 0x54: + return KeyCodes::KEY_T; + case 0x55: + return KeyCodes::KEY_U; + case 0x56: + return KeyCodes::KEY_V; + case 0x57: + return KeyCodes::KEY_W; + case 0x58: + return KeyCodes::KEY_X; + case 0x59: + return KeyCodes::KEY_Y; + case 0x5A: + return KeyCodes::KEY_Z; + case VK_OEM_4: + return KeyCodes::KEY_BRACKET_LEFT; + case VK_OEM_6: + return KeyCodes::KEY_BRACKET_RIGHT; + case VK_OEM_5: + return KeyCodes::KEY_BACKSLASH; + case VK_OEM_1: + return KeyCodes::KEY_SEMICOLON; + case VK_OEM_7: + return KeyCodes::KEY_APOSTROPHE; + case 0xBC: + return KeyCodes::KEY_COMMA; + case 0xBE: + return KeyCodes::KEY_DOT; + case VK_OEM_2: + return KeyCodes::KEY_SLASH; + + case VK_INSERT: + return KeyCodes::KEY_INSERT; + case VK_DELETE: + return KeyCodes::KEY_DELETE; + case VK_HOME: + return KeyCodes::KEY_HOME; + case VK_END: + return KeyCodes::KEY_END; + case VK_PRIOR: + return KeyCodes::KEY_PAGE_UP; + case VK_NEXT: + return KeyCodes::KEY_PAGE_DOWN; + + case VK_UP: + return KeyCodes::KEY_UP; + case VK_LEFT: + return KeyCodes::KEY_LEFT; + case VK_DOWN: + return KeyCodes::KEY_DOWN; + case VK_RIGHT: + return KeyCodes::KEY_RIGHT; + + case VK_NUMLOCK: + return KeyCodes::KEY_NUM_LOCK; + case VK_ADD: + return KeyCodes::KEY_NUM_ADD; + case VK_SUBTRACT: + return KeyCodes::KEY_NUM_SUBTRACT; + case VK_MULTIPLY: + return KeyCodes::KEY_NUM_MULTIPLY; + case VK_DIVIDE: + return KeyCodes::KEY_NUM_DIVIDE; + case VK_NUMPAD0: + return KeyCodes::KEY_NUM_0; + case VK_NUMPAD1: + return KeyCodes::KEY_NUM_1; + case VK_NUMPAD2: + return KeyCodes::KEY_NUM_2; + case VK_NUMPAD3: + return KeyCodes::KEY_NUM_3; + case VK_NUMPAD4: + return KeyCodes::KEY_NUM_4; + case VK_NUMPAD5: + return KeyCodes::KEY_NUM_5; + case VK_CLEAR: + return KeyCodes::KEY_NUM_SPECIAL; + case VK_NUMPAD6: + return KeyCodes::KEY_NUM_6; + case VK_NUMPAD7: + return KeyCodes::KEY_NUM_7; + case VK_NUMPAD8: + return KeyCodes::KEY_NUM_8; + case VK_NUMPAD9: + return KeyCodes::KEY_NUM_9; + case VK_DECIMAL: + return KeyCodes::KEY_NUM_DECIMAL; + default: + return KeyCodes::UNKNOWN_KEY; + } + } + + LRESULT __stdcall WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { + switch(message) { + case WM_SIZE: + { + //íå ðåñàéçèì äåâàéñ åñëè îêíî ñâ¸ðíóòî + if(wparam != SIZE_MINIMIZED) { + long val = GetWindowLongPtr(hwnd, GWLP_USERDATA); + if(val) { + WindowWin* wnd = (WindowWin*)val; + wnd->OnResize(); + + } + } + } + break; + case WM_SETCURSOR: + { + static bool hiddencursor = false; + WORD ht = LOWORD(lparam); + + long val = GetWindowLongPtr(hwnd, GWLP_USERDATA); + if(val) { + WindowWin* wnd = (WindowWin*)val; + if(wnd->IsCursorVisible()) { + ShowCursor(true); + } else { + if(HTCLIENT == ht && !hiddencursor) { + hiddencursor = true; + ShowCursor(false); + } else if(HTCLIENT != ht && hiddencursor) { + hiddencursor = false; + ShowCursor(true); + } + } + } + } + break; + case WM_ACTIVATEAPP: + { + //if(wparam == TRUE) + + long val = GetWindowLongPtr(hwnd, GWLP_USERDATA); + if(val) { + WindowWin* wnd = (WindowWin*)val; + + if(wparam!= 0) { + wnd->OnActivate(); + } else { + wnd->OnDeactivate(); + } + } + } + break; + case WM_ACTIVATE: //Catch Alt + Tab + { + switch(wparam) { + case WA_ACTIVE: + { + long val = GetWindowLongPtr(hwnd, GWLP_USERDATA); + if(val) { + WindowWin* wnd = (WindowWin*)val; + wnd->OnActivate(); + } + } + break; + case WA_INACTIVE: + { + long val = GetWindowLongPtr(hwnd, GWLP_USERDATA); + if(val) { + WindowWin* wnd = (WindowWin*)val; + wnd->OnDeactivate(); + } + } + break; + } + } + break; + case WM_DESTROY: + PostQuitMessage(0); + DestroyWindow(hwnd); + break; + case WM_SYSKEYDOWN: + switch(wparam) { + case VK_RETURN: + { + if(GetKeyState(VK_MENU)) //Alt + Enter + { + long val = GetWindowLongPtr(hwnd, GWLP_USERDATA); + if(val) { + WindowWin* wnd = (WindowWin*)val; + if(!wnd->IsAltEnterLocked()) { + wnd->SetAltEnterLocked(true); + wnd->SwitchMode(!wnd->IsFullScreen()); + } + } + } + } + break; + } + break; + case WM_SYSKEYUP: + { + switch(wparam) { + case VK_RETURN: + { + long val = GetWindowLongPtr(hwnd, GWLP_USERDATA); + if(val) { + WindowWin* wnd = (WindowWin*)val; + wnd->SetAltEnterLocked(false); + } + } + break; + } + } + break; + case WM_KEYDOWN: + switch(wparam) { + case VK_ESCAPE: + //PostQuitMessage(0); + exit(0); + break; + } + } + return DefWindowProc(hwnd, message, wparam, lparam); + } + +} \ No newline at end of file diff --git a/LifeCore/WindowWin.h b/LifeCore/WindowWin.h new file mode 100644 index 0000000..700b99b --- /dev/null +++ b/LifeCore/WindowWin.h @@ -0,0 +1,74 @@ +#pragma once + +#include +#include +#include +#include +#include "Window.h" + + +namespace LifeCore{ + class WindowWin : public Window + { + public: + /*virtual ~RenderWindow(void) {} + virtual void Update() = 0; + virtual void SetCaption(const String& caption) = 0; + virtual int GetWindowHandle() = 0; + virtual int GetWidth() = 0; + virtual int GetHeight() = 0; + virtual const String& GetCaption() = 0;*/ + + WindowWin(); + ~WindowWin(void); + bool IsKeyPressed(KeyCodes::Key key); + //void AddWindowDemander(IWindowDemander* demander); + void Update(); + void SetCaption(const String& caption); + int GetWindowHandle(){return (int)g_hwnd;} + void SetSize(int w, int h); + bool IsFullScreen(); + void SetFullScreen(bool state); + bool SwitchMode(bool fullscreen); + void SetFullscreenMode(LifeCore::DisplayMode mode); + + void OnActivate(); + void OnDeactivate(); + void OnResize(); + //void OnInput(); + bool IsActive(); + bool IsModeSwitching(); + void SetCursorVisible(bool state); + bool IsCursorVisible(); + void CenterWindow(); + + int GetWidth() + { + return width; + } + virtual int GetHeight() + { + return height; + } + const String& GetCaption() + { + return caption; + } + void SetAltEnterLocked(bool state) + { + bAltEnterLocked = state; + } + bool IsAltEnterLocked() + { + return bAltEnterLocked; + } + protected: + KeyCodes::Key ConvertKeyCode(unsigned long key_code); + std::vector window_demanders; + bool bAltEnterLocked; + HINSTANCE g_hInst; + HWND g_hwnd; + DEVMODE mode; + }; +} + diff --git a/LifeCore/delegate/Delegate.h b/LifeCore/delegate/Delegate.h new file mode 100644 index 0000000..f6977fc --- /dev/null +++ b/LifeCore/delegate/Delegate.h @@ -0,0 +1,125 @@ +#ifndef Delegate_h__ +#define Delegate_h__ + +class CDelegate +{ +}; + +// 0 parameters... +#define SUFFIX 0 +#define TEMPLATE_PARAMS +#define TEMPLATE_ARGS +#define PARAMS +#define ARGS + +#include "Delegate_base.h" + +#undef SUFFIX +#undef TEMPLATE_PARAMS +#undef TEMPLATE_ARGS +#undef PARAMS +#undef ARGS + +// 1 parameter... +#define SUFFIX 1 +#define TEMPLATE_PARAMS \ + , class TP1 +#define TEMPLATE_ARGS , TP1 +#define PARAMS TP1 p1 +#define ARGS p1 + +#include "Delegate_base.h" + +#undef SUFFIX +#undef TEMPLATE_PARAMS +#undef TEMPLATE_ARGS +#undef PARAMS +#undef ARGS + +// 2 parameters... +#define SUFFIX 2 +#define TEMPLATE_PARAMS \ + , class TP1, class TP2 +#define TEMPLATE_ARGS \ + ,TP1, TP2 +#define PARAMS TP1 p1, TP2 p2 +#define ARGS p1, p2 + +#include "Delegate_base.h" + +#undef SUFFIX +#undef TEMPLATE_PARAMS +#undef TEMPLATE_ARGS +#undef PARAMS +#undef ARGS + +// 3 parameters... +#define SUFFIX 3 +#define TEMPLATE_PARAMS \ + , class TP1, class TP2, class TP3 +#define TEMPLATE_ARGS \ + ,TP1, TP2, TP3 +#define PARAMS TP1 p1, TP2 p2, TP3 p3 +#define ARGS p1, p2, p3 + +#include "Delegate_base.h" + +#undef SUFFIX +#undef TEMPLATE_PARAMS +#undef TEMPLATE_ARGS +#undef PARAMS +#undef ARGS + +// 4 parameters... +#define SUFFIX 4 +#define TEMPLATE_PARAMS \ + , class TP1, class TP2, class TP3, class TP4 +#define TEMPLATE_ARGS \ + ,TP1, TP2, TP3, TP4 +#define PARAMS TP1 p1, TP2 p2, TP3 p3, TP4 p4 +#define ARGS p1, p2, p3, p4 + +#include "Delegate_base.h" + +#undef SUFFIX +#undef TEMPLATE_PARAMS +#undef TEMPLATE_ARGS +#undef PARAMS +#undef ARGS + +// 5 parameters... +#define SUFFIX 5 +#define TEMPLATE_PARAMS \ + , class TP1, class TP2, class TP3, class TP4, class TP5 +#define TEMPLATE_ARGS \ + ,TP1, TP2, TP3, TP4, TP5 +#define PARAMS TP1 p1, TP2 p2, TP3 p3, TP4 p4, TP5 p5 +#define ARGS p1, p2, p3, p4, p5 + +#include "Delegate_base.h" + +#undef SUFFIX +#undef TEMPLATE_PARAMS +#undef TEMPLATE_ARGS +#undef PARAMS +#undef ARGS + +//event declarations +#define EVENT(NAME) typedef Event0 NAME; + +#define EVENT0(NAME) typedef Event0 NAME; //same as previous + +#define EVENT1(NAME, P1) typedef Event1 NAME; + +#define EVENT2(NAME, P1, P2) typedef Event2 NAME; + +#define EVENT3(NAME, P1, P2, P3) typedef Event3 NAME; + +#define EVENT4(NAME, P1, P2, P3, P4) typedef Event4 NAME; + +#define EVENT5(NAME, P1, P2, P3, P4, P5) typedef Event5 NAME; + +//delegate declarations +#define DELEGATE0(NAME) typedef Delegate0 ##NAME; + +#endif // Delegate_h__ \ No newline at end of file diff --git a/LifeCore/delegate/Delegate_base.h b/LifeCore/delegate/Delegate_base.h new file mode 100644 index 0000000..bad3224 --- /dev/null +++ b/LifeCore/delegate/Delegate_base.h @@ -0,0 +1,164 @@ +#include + +#define COMBINE(a,b) COMBINE1(a,b) +#define COMBINE1(a,b) a##b + +#define COMBINE3(a,b,c) COMBINE3(a,b,c) +#define COMBINE31(a,b,c) a##b##c + +#define I_DELEGATE COMBINE(Delegate, SUFFIX) +#define I_EVENT COMBINE(Event, SUFFIX) +#define C_STATIC_DELEGATE COMBINE(StaticDelegate, SUFFIX) +#define C_METHOD_DELEGATE COMBINE(MethodDelegate, SUFFIX) + +template +class I_DELEGATE : public CDelegate +{ +public: + virtual ~I_DELEGATE(){} + virtual TRet Invoke(PARAMS) = 0; + virtual bool Compare(I_DELEGATE* other) = 0; +}; + +template +class C_STATIC_DELEGATE : public I_DELEGATE +{ +public: + typedef TRet (*Func)(PARAMS); + C_STATIC_DELEGATE(Func func) + { + pFunc = func; + } + C_STATIC_DELEGATE(const C_STATIC_DELEGATE& other) + { + pFunc = other.pFunc; + } + const C_STATIC_DELEGATE& operator=(const C_STATIC_DELEGATE& other) + { + this->pFunc = other.pFunc; + return *this; + } + virtual TRet Invoke(PARAMS) + { + return pFunc(ARGS); + } + virtual bool Compare(I_DELEGATE* other) + { + return true; + } +private: + Func pFunc; +}; + +template +class C_METHOD_DELEGATE : public I_DELEGATE +{ +public: + typedef TRet (TBase::*Func)(PARAMS); + + C_METHOD_DELEGATE(TBase* base, Func func) + { + pBase = base; + pFunc = func; + } + C_METHOD_DELEGATE(const C_METHOD_DELEGATE& other) + { + pBase = other.pBase; + pFunc = other.pFunc; + } + const C_METHOD_DELEGATE& operator=(const C_METHOD_DELEGATE& other) + { + this->pFunc = other.pFunc; + this->pBase = other.pBase; + return *this; + } + virtual TRet Invoke(PARAMS) + { + return (pBase->*pFunc)(ARGS); + } + virtual bool Compare(I_DELEGATE* other) + { + return true; + } +private: + Func pFunc; + TBase* pBase; +}; + +template +__forceinline I_DELEGATE* NewDelegate(TRet (*pFunc)(PARAMS)) +{ + return new C_STATIC_DELEGATE(pFunc); +} + +template +__forceinline I_DELEGATE* NewDelegate(TBase* base, TRet (TBase::*pFunc)(PARAMS)) +{ + return new C_METHOD_DELEGATE(base, pFunc); +} + +template +class I_EVENT +{ +public: + typedef I_DELEGATE IDelegate; + typedef std::list DelegateList; + + DelegateList delegates; + + void Clear() + { + for(DelegateList::iterator it = delegates.begin(); it != delegates.end(); ++it) + { + delete (*it); + } + delegates.clear(); + } + + void Remove(CDelegate* del) + { + for(DelegateList::iterator it = delegates.begin(); it != delegates.end(); ++it) + { + if(del == (*it)) + { + delete (*it); + delegates.erase(it); + break; + } + } + } + + void AddDelegate(IDelegate* pDelegate) + { + if(pDelegate) + { + delegates.push_back(pDelegate); + } + } + + void Invoke(PARAMS) + { + for(DelegateList::iterator it = delegates.begin(); it != delegates.end(); ++it) + { + (*it)->Invoke(ARGS); + } + } + + I_EVENT& operator =(IDelegate* pDelegate) + { + Clear(); + AddDelegate(pDelegate); + return *this; + } + I_EVENT& operator +=(IDelegate* pDelegate) + { + AddDelegate(pDelegate); + return *this; + } + + I_EVENT& operator +=(CDelegate* pDelegate) + { + AddDelegate((IDelegate*)pDelegate); + return *this; + } +}; \ No newline at end of file diff --git a/LifeCore/log/CLog.cpp b/LifeCore/log/CLog.cpp new file mode 100644 index 0000000..336db47 --- /dev/null +++ b/LifeCore/log/CLog.cpp @@ -0,0 +1,33 @@ +#include "CLog.h" + +namespace LifeCore +{ + CLog::CLog() + :logToConsole(true) + { + hLogFile = CreateFile(String(_text("engine_log.txt")).c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + SetFilePointer(hLogFile,0,NULL, FILE_END); + Write(String(_text("\nLogging started!")).c_str()); + } + void CLog::LogToConsole(bool condition) + { + logToConsole = condition; + } + CLog::~CLog() + { + if(hLogFile) + { + Write(_text("Closing log")); + CloseHandle(hLogFile); + } + } + void CLog::Write(const String& message) + { + DWORD nb = 0; + WriteFile(hLogFile, message.c_str(), string_size(message), &nb, NULL); + WriteFile(hLogFile, L"\n", 1, &nb, NULL); + + if(logToConsole) + printf("Log: %s\n", message.c_str()); + } +} \ No newline at end of file diff --git a/LifeCore/log/CLog.h b/LifeCore/log/CLog.h new file mode 100644 index 0000000..2f382c0 --- /dev/null +++ b/LifeCore/log/CLog.h @@ -0,0 +1,28 @@ +#ifndef CLOG_H__ +#define CLOG_H__ + +#include "../Singleton.h" +#include "../LString.h" +#include + +namespace LifeCore +{ +#define LOG_MESSAGE(message) LifeCore::CLog::me()->Write(STR(##message##)) +#define LOG_ERROR(message, module, method) LifeCore::CLog::me()->Write("Error occured in method: " + STR(##module##) + "::" + STR(##method##) + "\n" + STR(##message##)) + + class CLog; + + class CLog : public Singleton + { + public: + CLog(); + ~CLog(); + void Write(const String& message); + void LogToConsole(bool condition); + private: + HANDLE hLogFile; + bool logToConsole; + }; +} + +#endif \ No newline at end of file diff --git a/LifeCore/log/NLog.cpp b/LifeCore/log/NLog.cpp new file mode 100644 index 0000000..bb829ec --- /dev/null +++ b/LifeCore/log/NLog.cpp @@ -0,0 +1,94 @@ +#include "NLog.h" +//#include + +#include + +#define WIN32_LEAN_AND_MEAN +#include +#include + +namespace LifeCore { + NettleLog::NettleLog(LOG_LEVEL _level) + : AbortOnError(true), bOutConsole(true), bOutFile(false), bOutInterface(false), level(_level) { + //TODO: remove allocation of stringstream + stm = new std::stringstream(); + } + NettleLog::~NettleLog() { + //std::stringstream stm; + *stm << "\n"; + /*stm->seekg (0, std::ios::end); + + size_t length = (size_t)stm->tellg(); + stm->seekg (0, std::ios::beg); + + char* buffer = new char [length + 1]; + buffer[length] = 0; + stm->read (buffer,length);*/ + //_write(1, buffer, length); + std::string str = stm->str(); + std::cout << str; + +#if defined(NOS_WINDOWS) && defined(_DEBUG) + OutputDebugString(str.c_str()); +#endif + + delete stm; + // delete[] buffer; + } + NettleLog& NettleLog::operator <<(bool val) { + *stm << (val ? "true" : "false"); + return *this; + } + NettleLog& NettleLog::operator <<(uint32 val) { + *stm << val; + return *this; + } + NettleLog& NettleLog::operator <<(long val) { + *stm << val; + return *this; + } + NettleLog& NettleLog::operator <<(unsigned long val) { + *stm << val; + return *this; + } + NettleLog& NettleLog::operator <<(uint16 val) { + *stm << val; + return *this; + } + NettleLog& NettleLog::operator <<(uint64 val) { + *stm << val; + return *this; + } + NettleLog& NettleLog::operator <<(int64 val) { + *stm << val; + return *this; + } + NettleLog& NettleLog::operator <<(int32 val) { + *stm << val; + return *this; + } + NettleLog& NettleLog::operator <<(int16 val) { + *stm << val; + return *this; + } + NettleLog& NettleLog::operator <<(int8 val) { + *stm << val; + return *this; + } + NettleLog& NettleLog::operator <<(float val) { + *stm << val; + return *this; + } + NettleLog& NettleLog::operator <<(double val) { + *stm << val; + return *this; + } + NettleLog& NettleLog::operator <<(const String& val) { + *stm << val.c_str(); + return *this; + } + NettleLog& NettleLog::operator <<(const char* val) { + *stm << val; + return *this; + } +} diff --git a/LifeCore/log/NLog.h b/LifeCore/log/NLog.h new file mode 100644 index 0000000..9ad35f3 --- /dev/null +++ b/LifeCore/log/NLog.h @@ -0,0 +1,55 @@ +#ifndef NLog_h__ +#define NLog_h__ + +#include +#include +#include +#include +#include "../Types.h" + +namespace LifeCore { + enum LOG_LEVEL { + LOG_WARNING, + LOG_ERROR, + LOG_INFO + }; + + class ILogOutput { + public: + virtual bool Write(LOG_LEVEL level, const String& message) = 0; + }; + + class NettleLog { + public: + std::stringstream* stm; + //std::stringstream stm; + NettleLog(LOG_LEVEL _level); + ~NettleLog(); + NettleLog& operator <<(int8 val); + NettleLog& operator <<(int16 val); + NettleLog& operator <<(uint16 val); + NettleLog& operator <<(int32 val); + NettleLog& operator <<(uint32 val); + NettleLog& operator <<(uint64 val); + NettleLog& operator <<(int64 val); + NettleLog& operator <<(long val); + NettleLog& operator <<(unsigned long val); + NettleLog& operator <<(float val); + NettleLog& operator <<(double val); + NettleLog& operator <<(const String& val); + NettleLog& operator <<(const char* val); + NettleLog& operator <<(bool val); + private: + LOG_LEVEL level; + + bool AbortOnError; + bool bOutConsole; + bool bOutFile; + bool bOutInterface; + }; + + inline NettleLog LogWarning() { return NettleLog(LOG_WARNING); } + inline NettleLog LogError() { return NettleLog(LOG_ERROR); } + inline NettleLog LogInfo() { return NettleLog(LOG_INFO); } +} +#endif // NLog_h__ diff --git a/LifeCore/math/LifeMath.cpp b/LifeCore/math/LifeMath.cpp new file mode 100644 index 0000000..3069ee9 --- /dev/null +++ b/LifeCore/math/LifeMath.cpp @@ -0,0 +1,125 @@ +#include "LifeMath.h" + +namespace LifeMath +{ + extern INLINE void Mat4x4Invert(const Matrix4x4* matrix, Matrix4x4* result) + { + float num5 = matrix->M11; + float num4 = matrix->M12; + float num3 = matrix->M13; + float num2 = matrix->M14; + float num9 = matrix->M21; + float num8 = matrix->M22; + float num7 = matrix->M23; + float num6 = matrix->M24; + float num17 = matrix->M31; + float num16 = matrix->M32; + float num15 = matrix->M33; + float num14 = matrix->M34; + float num13 = matrix->M41; + float num12 = matrix->M42; + float num11 = matrix->M43; + float num10 = matrix->M44; + float num23 = (num15 * num10) - (num14 * num11); + float num22 = (num16 * num10) - (num14 * num12); + float num21 = (num16 * num11) - (num15 * num12); + float num20 = (num17 * num10) - (num14 * num13); + float num19 = (num17 * num11) - (num15 * num13); + float num18 = (num17 * num12) - (num16 * num13); + float num39 = ((num8 * num23) - (num7 * num22)) + (num6 * num21); + float num38 = -(((num9 * num23) - (num7 * num20)) + (num6 * num19)); + float num37 = ((num9 * num22) - (num8 * num20)) + (num6 * num18); + float num36 = -(((num9 * num21) - (num8 * num19)) + (num7 * num18)); + float num = 1.0f / ((((num5 * num39) + (num4 * num38)) + (num3 * num37)) + (num2 * num36)); + result->M11 = num39 * num; + result->M21 = num38 * num; + result->M31 = num37 * num; + result->M41 = num36 * num; + result->M12 = -(((num4 * num23) - (num3 * num22)) + (num2 * num21)) * num; + result->M22 = (((num5 * num23) - (num3 * num20)) + (num2 * num19)) * num; + result->M32 = -(((num5 * num22) - (num4 * num20)) + (num2 * num18)) * num; + result->M42 = (((num5 * num21) - (num4 * num19)) + (num3 * num18)) * num; + float num35 = (num7 * num10) - (num6 * num11); + float num34 = (num8 * num10) - (num6 * num12); + float num33 = (num8 * num11) - (num7 * num12); + float num32 = (num9 * num10) - (num6 * num13); + float num31 = (num9 * num11) - (num7 * num13); + float num30 = (num9 * num12) - (num8 * num13); + result->M13 = (((num4 * num35) - (num3 * num34)) + (num2 * num33)) * num; + result->M23 = -(((num5 * num35) - (num3 * num32)) + (num2 * num31)) * num; + result->M33 = (((num5 * num34) - (num4 * num32)) + (num2 * num30)) * num; + result->M43 = -(((num5 * num33) - (num4 * num31)) + (num3 * num30)) * num; + float num29 = (num7 * num14) - (num6 * num15); + float num28 = (num8 * num14) - (num6 * num16); + float num27 = (num8 * num15) - (num7 * num16); + float num26 = (num9 * num14) - (num6 * num17); + float num25 = (num9 * num15) - (num7 * num17); + float num24 = (num9 * num16) - (num8 * num17); + result->M14 = -(((num4 * num29) - (num3 * num28)) + (num2 * num27)) * num; + result->M24 = (((num5 * num29) - (num3 * num26)) + (num2 * num25)) * num; + result->M34 = -(((num5 * num28) - (num4 * num26)) + (num2 * num24)) * num; + result->M44 = (((num5 * num27) - (num4 * num25)) + (num3 * num24)) * num; + } + + extern INLINE void QuatRotationYawPitchRoll(float yaw, float pitch, float roll, Quaternion& result) + { + float num13 = roll * 0.5f; + float num2 = sinf(num13); + float num = cosf(num13); + float num12 = pitch * 0.5f; + float num11 = sinf(num12); + float num10 = cosf(num12); + float num9 = yaw * 0.5f; + float num8 = sinf(num9); + float num7 = cosf(num9); + float num6 = num7 * num11; + float num5 = num8 * num10; + result.X = ((num2 * num5) + (num * num6)); + result.Y = ((num * num5) - (num2 * num6)); + float num4 = num7 * num10; + float num3 = num8 * num11; + result.Z = ((num2 * num4) - (num * num3)); + result.W = ((num2 * num3) + (num * num4)); + } + + extern INLINE void QuatRotationMatrix(const Matrix4x4& matrix, Quaternion& dst) + { + float num8 = (matrix.M11 + matrix.M22) + matrix.M33; + if (num8 > 0.0f) + { + float num = sqrtf(num8 + 1.0f); + dst.W = num * 0.5f; + num = 0.5f / num; + dst.X = (matrix.M23 - matrix.M32) * num; + dst.Y = (matrix.M31 - matrix.M13) * num; + dst.Z = (matrix.M12 - matrix.M21) * num; + } + else if ((matrix.M11 >= matrix.M22) && (matrix.M11 >= matrix.M33)) + { + float num7 = sqrtf((((1.0f + matrix.M11) - matrix.M22) - matrix.M33)); + float num4 = 0.5f / num7; + dst.X = 0.5f * num7; + dst.Y = (matrix.M12 + matrix.M21) * num4; + dst.Z = (matrix.M13 + matrix.M31) * num4; + dst.W = (matrix.M23 - matrix.M32) * num4; + } + else if (matrix.M22 > matrix.M33) + { + float num6 = sqrtf((((1.0f + matrix.M22) - matrix.M11) - matrix.M33)); + float num3 = 0.5f / num6; + dst.X = (matrix.M21 + matrix.M12) * num3; + dst.Y = 0.5f * num6; + dst.Z = (matrix.M32 + matrix.M23) * num3; + dst.W = (matrix.M31 - matrix.M13) * num3; + } + else + { + float num5 = sqrtf((((1.0f + matrix.M33) - matrix.M11) - matrix.M22)); + float num2 = 0.5f / num5; + dst.X = (matrix.M31 + matrix.M13) * num2; + dst.Y = (matrix.M32 + matrix.M23) * num2; + dst.Z = 0.5f * num5; + dst.W = (matrix.M12 - matrix.M21) * num2; + } + } +} \ No newline at end of file diff --git a/LifeCore/math/LifeMath.h b/LifeCore/math/LifeMath.h new file mode 100644 index 0000000..c502bfe --- /dev/null +++ b/LifeCore/math/LifeMath.h @@ -0,0 +1,1428 @@ +#ifndef LifeMath_h__ +#define LifeMath_h__ + +//#include "LifeDebug.h" + +#include "Math.h" +#include +#include +#include "Vector2D.h" +#include "Vector3D.h" +#include "Vector4D.h" +#include "Matrix4x4.h" +#include "Quaternion.h" +#include "Plane.h" +#include + +inline int GetFlag(int val, int flag) +{ + return val & flag; +} +inline void RemoveFlag(int& val, int flag) +{ + val = val & (~flag); +} +inline void SetFlag(int& val, int flag) +{ + val |= flag; +} +namespace LifeMath +{ +#undef INLINE +#ifdef _WIN32 +#ifdef _MSC_VER +#define INLINE __forceinline +#endif +#else +#ifdef __GNUC__ +#define INLINE __attribute__((always_inline)) +#endif +#endif + +#ifndef INLINE +#define INLINE inline +#endif + + /*#define min(x, y) \ + (x < y ? x : y) + + #define max(x, y) \ + (x > y ? x : y)*/ + + /*template + INLINE const T& (min)(const T& left, const T& right) + { + return left < right ? left : right; + } + + template + INLINE T max(T left, T right) + { + return left > right ? left : right; + }*/ + + static const float PI = 3.14159265358979f; + enum Axis + { + X, Y, Z, W + }; + + /*template + T SetFlag(T val, T flag) + { + return val | flag; + } + + template + T GetFlag(T val, T flag) + { + return val & flag; + }*/ + + /*template + T RemoveFlag(T val, T flag) + { + return val & (~flag); + }*/ + + extern void Mat4x4Invert(const Matrix4x4* matrix, Matrix4x4* result); + extern void QuatRotationYawPitchRoll(float yaw, float pitch, float roll, Quaternion& result); + extern void QuatRotationMatrix(const Matrix4x4& matrix, Quaternion& dst); + + //----- Vector3D----- + INLINE float Vec3Length(Vec3f* src) + { + return sqrtf(src->X * src->X + src->Y * src->Y + src->Z * src->Z); + } + + INLINE float Vec3Length(const Vec3f& src) + { + return sqrtf(src.X * src.X + src.Y * src.Y + src.Z * src.Z); + } + + INLINE void Vec3Normalize(Vec3f* src) + { + float num2 = Vec3Length(src); + if (num2 != 0.0f) + { + float num = 1.0f / num2; + src->X *= num; + src->Y *= num; + src->Z *= num; + } + } + + INLINE void Vec3Normalize(Vec3f* vector, Vec3f* result) + { + float num2 = Vec3Length(vector); + if (num2 != 0.0f) + { + float num = 1.0f / num2; + result->X = vector->X * num; + result->Y = vector->Y * num; + result->Z = vector->Z * num; + } + else + { + result->X = result->Y = result->Z = 0.0f; + } + } + + INLINE void Vec3Add(Vec3f& src, Vec3f& val) + { + src.X += val.X; + src.Y += val.Y; + src.Z += val.Z; + } + + INLINE void Vec3Reflect(Vec3f& vector, Vec3f& normal, Vec3f& result) + { + float num2 = ((vector.Y * normal.Y) + (vector.X * normal.X)) + (vector.Z * normal.Z); + float num = num2 * 2.0f; + result.X = vector.X - (normal.X * num); + result.Y = vector.Y - (normal.Y * num); + result.Z = vector.Z - (normal.Z * num); + } + + INLINE void Vec3Reflect(Vec3f& vector, Vec3f& normal) + { + float num2 = ((vector.Y * normal.Y) + (vector.X * normal.X)) + (vector.Z * normal.Z); + float num = num2 * 2.0f; + vector.X = vector.X - (normal.X * num); + vector.Y = vector.Y - (normal.Y * num); + vector.Z = vector.Z - (normal.Z * num); + } + + INLINE void Vec3Transform(Vec3f* vector, Matrix4x4* transformation, Vec4f* result) + { + result->X = (((vector->Y * transformation->M21) + (vector->X * transformation->M11)) + (vector->Z * transformation->M31)) + transformation->M41; + result->Y = (((vector->Y * transformation->M22) + (vector->X * transformation->M12)) + (vector->Z * transformation->M32)) + transformation->M42; + result->Z = (((vector->Y * transformation->M23) + (vector->X * transformation->M13)) + (vector->Z * transformation->M33)) + transformation->M43; + result->W = (((transformation->M24 * vector->Y) + (transformation->M14 * vector->X)) + (vector->Z * transformation->M34)) + transformation->M44; + } + + INLINE float Vec3Dot(const Vec3f* left, const Vec3f* right) + { + return (((left->Y * right->Y) + (left->X * right->X)) + (left->Z * right->Z)); + } + + INLINE Vec3f Vec3TransformCoordinate(const Matrix4x4& transformation, const Vec3f& coordinate) + { + Vec3f vector; + Vec4f vector2; + vector2.X = (((transformation.M21 * coordinate.Y) + (transformation.M11 * coordinate.X)) + (transformation.M31 * coordinate.Z)) + transformation.M41; + vector2.Y = (((transformation.M22 * coordinate.Y) + (transformation.M12 * coordinate.X)) + (transformation.M32 * coordinate.Z)) + transformation.M42; + vector2.Z = (((transformation.M23 * coordinate.Y) + (transformation.M13 * coordinate.X)) + (transformation.M33 * coordinate.Z)) + transformation.M43; + float num = (1.0f / ((((transformation.M24 * coordinate.Y) + (transformation.M14 * coordinate.X)) + (transformation.M34 * coordinate.Z)) + transformation.M44)); + vector2.W = num; + vector.X = vector2.X * num; + vector.Y = vector2.Y * num; + vector.Z = vector2.Z * num; + return vector; + } + + INLINE void Vec3TransformCoordinate(Vec3f& coordinate, const Matrix4x4& transformation) + { + Vec4f vector2; + vector2.X = (((transformation.M21 * coordinate.Y) + (transformation.M11 * coordinate.X)) + (transformation.M31 * coordinate.Z)) + transformation.M41; + vector2.Y = (((transformation.M22 * coordinate.Y) + (transformation.M12 * coordinate.X)) + (transformation.M32 * coordinate.Z)) + transformation.M42; + vector2.Z = (((transformation.M23 * coordinate.Y) + (transformation.M13 * coordinate.X)) + (transformation.M33 * coordinate.Z)) + transformation.M43; + float num = (float)(1.0 / ((((transformation.M24 * coordinate.Y) + (transformation.M14 * coordinate.X)) + (transformation.M34 * coordinate.Z)) + transformation.M44)); + vector2.W = num; + coordinate.X = vector2.X * num; + coordinate.Y = vector2.Y * num; + coordinate.Z = vector2.Z * num; + } + + INLINE Vec3f Vec3TransformNoW(const Vec3f& coordinate, const Matrix4x4& transformation) + { + Vec3f vector; + vector.X = (((transformation.M21 * coordinate.Y) + (transformation.M11 * coordinate.X)) + (transformation.M31 * coordinate.Z)) + transformation.M41; + vector.Y = (((transformation.M22 * coordinate.Y) + (transformation.M12 * coordinate.X)) + (transformation.M32 * coordinate.Z)) + transformation.M42; + vector.Z = (((transformation.M23 * coordinate.Y) + (transformation.M13 * coordinate.X)) + (transformation.M33 * coordinate.Z)) + transformation.M43; + return vector; + } + + INLINE void Vec3Minimize(const Vec3f& value1, const Vec3f& value2, Vec3f& result) + { + float z; + float y; + float x; + if (value1.X < value2.X) + { + x = value1.X; + } + else + { + x = value2.X; + } + result.X = x; + if (value1.Y < value2.Y) + { + y = value1.Y; + } + else + { + y = value2.Y; + } + result.Y = y; + if (value1.Z < value2.Z) + { + z = value1.Z; + } + else + { + z = value2.Z; + } + result.Z = z; + } + + INLINE void Vec3Minimize(Vec4f& value1, Vec3f& value2, Vec3f& result) + { + float z; + float y; + float x; + if (value1.X < value2.X) + { + x = value1.X; + } + else + { + x = value2.X; + } + result.X = x; + if (value1.Y < value2.Y) + { + y = value1.Y; + } + else + { + y = value2.Y; + } + result.Y = y; + if (value1.Z < value2.Z) + { + z = value1.Z; + } + else + { + z = value2.Z; + } + result.Z = z; + } + + INLINE void Vec3Maximize(const Vec3f& value1, const Vec3f& value2, Vec3f& result) + { + float z; + float y; + float x; + if (value1.X > value2.X) + { + x = value1.X; + } + else + { + x = value2.X; + } + result.X = x; + if (value1.Y > value2.Y) + { + y = value1.Y; + } + else + { + y = value2.Y; + } + result.Y = y; + if (value1.Z > value2.Z) + { + z = value1.Z; + } + else + { + z = value2.Z; + } + result.Z = z; + } + + INLINE void Vec3Maximize(const Vec4f& value1, const Vec3f& value2, Vec3f& result) + { + float z; + float y; + float x; + if (value1.X > value2.X) + { + x = value1.X; + } + else + { + x = value2.X; + } + result.X = x; + if (value1.Y > value2.Y) + { + y = value1.Y; + } + else + { + y = value2.Y; + } + result.Y = y; + if (value1.Z > value2.Z) + { + z = value1.Z; + } + else + { + z = value2.Z; + } + result.Z = z; + } + + INLINE void Vec3TransformCoordinate(const Vec3f& coordinate, const Matrix4x4& transformation, Vec3f& result) + { + Vec4f vector2; + vector2.X = (((coordinate.Y * transformation.M21) + (coordinate.X * transformation.M11)) + (coordinate.Z * transformation.M31)) + transformation.M41; + vector2.Y = (((coordinate.Y * transformation.M22) + (coordinate.X * transformation.M12)) + (coordinate.Z * transformation.M32)) + transformation.M42; + vector2.Z = (((coordinate.Y * transformation.M23) + (coordinate.X * transformation.M13)) + (coordinate.Z * transformation.M33)) + transformation.M43; + float num = (float)(1.0 / ((((transformation.M24 * coordinate.Y) + (transformation.M14 * coordinate.X)) + (coordinate.Z * transformation.M34)) + transformation.M44)); + vector2.W = num; + result.X = vector2.X * num; + result.Y = vector2.Y * num; + result.Z = vector2.Z * num; + } + + INLINE void Vec3Cross(const Vec3f& left, const Vec3f& right, Vec3f& result) + { + result.X = (left.Y * right.Z) - (left.Z * right.Y); + result.Y = (left.Z * right.X) - (left.X * right.Z); + result.Z = (left.X * right.Y) - (left.Y * right.X); + } + + INLINE Vec3f Vec3Cross(const Vec3f& vector1, const Vec3f& vector2) + { + Vec3f vector; + vector.X = (vector1.Y * vector2.Z) - (vector1.Z * vector2.Y); + vector.Y = (vector1.Z * vector2.X) - (vector1.X * vector2.Z); + vector.Z = (vector1.X * vector2.Y) - (vector1.Y * vector2.X); + return vector; + } + + INLINE Vec3f Vec3Lerp(const Vec3f& start, const Vec3f& end, float amount) + { + Vec3f vector; + vector.X = ((end.X - start.X) * amount) + start.X; + vector.Y = ((end.Y - start.Y) * amount) + start.Y; + vector.Z = ((end.Z - start.Z) * amount) + start.Z; + return vector; + } + + INLINE void Vec3Lerp(const Vec3f& start, const Vec3f& end, float amount, Vec3f& result) + { + result.X = ((end.X - start.X) * amount) + start.X; + result.Y = ((end.Y - start.Y) * amount) + start.Y; + result.Z = ((end.Z - start.Z) * amount) + start.Z; + } + + INLINE void Vec3Move(Vec3f& src, const Vec3f& direction, float amount) + { + src.X += direction.X * amount; + src.Y += direction.Y * amount; + src.Z += direction.Z * amount; + } + + INLINE void Vec3Mul(Vec3f& vector, float val) + { + vector.X *= val; + vector.Y *= val; + vector.Z *= val; + } + + INLINE void Vec3TransformCoordinate(Vec3f& coord, Matrix4x4& transform) + { + Vec4f vector; + vector.X = (((coord.X * transform.M11) + (coord.Y * transform.M21)) + (coord.Z * transform.M31)) + transform.M41; + vector.Y = (((coord.X * transform.M12) + (coord.Y * transform.M22)) + (coord.Z * transform.M32)) + transform.M42; + vector.Z = (((coord.X * transform.M13) + (coord.Y * transform.M23)) + (coord.Z * transform.M33)) + transform.M43; + vector.W = 1.0f / ((((coord.X * transform.M14) + (coord.Y * transform.M24)) + (coord.Z * transform.M34)) + transform.M44); + //vector.W = 1.0f; + coord.X = vector.X * vector.W; + coord.Y = vector.Y * vector.W; + coord.Z = vector.Z * vector.W; + } + + INLINE float Vec3Distance(const Vec3f& left, const Vec3f& right) + { + float x = right.X - left.X; + float y = right.Y - left.Y; + float z = right.Z - left.Z; + + return (float)sqrtf(x * x + y * y + z * z); + } + + INLINE float Vec3DistanceSquared(const Vec3f& left, const Vec3f& right) + { + float x = right.X - left.X; + float y = right.Y - left.Y; + float z = right.Z - left.Z; + + return x * x + y * y + z * z; + } + + INLINE float Vec3DistanceSquared(const Vec3f& left, const Vec4f& right) + { + float x = right.X - left.X; + float y = right.Y - left.Y; + float z = right.Z - left.Z; + + return x * x + y * y + z * z; + } + + INLINE Vec3f Vec3Unproject(const Vec3f& vector, float x, float y, float width, float height, float minZ, float maxZ, const Matrix4x4& worldViewProjection) + { + Vec3f coordinate; + Matrix4x4 transformation; + Mat4x4Invert(&worldViewProjection, &transformation); + + coordinate.X = ((((vector.X - x) / (width)) * 2.0f) - 1.0f); + coordinate.Y = -((((vector.Y - y) / (height)) * 2.0f) - 1.0f); + coordinate.Z = (vector.Z - minZ) / (maxZ - minZ); + Vec3TransformCoordinate(coordinate, transformation); + + return coordinate; + } + //-----Vector4D----- + INLINE void Vec4Move( Vec3f& source, Vec3f& direction, float amount) + { + source.X += direction.X * amount; + source.Y += direction.Y * amount; + source.Z += direction.Z * amount; + } + INLINE void Vec4Minimize(Vec4f& value1, Vec4f& value2, Vec4f& result) + { + float z; + float y; + float x; + float w; + if (value1.X < value2.X) + { + x = value1.X; + } + else + { + x = value2.X; + } + result.X = x; + if (value1.Y < value2.Y) + { + y = value1.Y; + } + else + { + y = value2.Y; + } + result.Y = y; + if (value1.Z < value2.Z) + { + z = value1.Z; + } + else + { + z = value2.Z; + } + result.Z = z; + if (value1.W < value2.W) + { + w = value1.W; + } + else + { + w = value2.W; + } + result.W = w; + } + INLINE void Vec4Maximize(Vec4f& value1, Vec4f& value2, Vec4f& result) + { + float z; + float y; + float x; + float w; + if (value1.X > value2.X) + { + x = value1.X; + } + else + { + x = value2.X; + } + result.X = x; + if (value1.Y > value2.Y) + { + y = value1.Y; + } + else + { + y = value2.Y; + } + result.Y = y; + if (value1.Z > value2.Z) + { + z = value1.Z; + } + else + { + z = value2.Z; + } + result.Z = z; + + if (value1.W > value2.W) + { + w = value1.W; + } + else + { + w = value2.W; + } + result.W = w; + } + //-----Matrix4x4----- + INLINE Matrix4x4* Mat4x4Identity(Matrix4x4* dst) + { + memset(&dst->M12, 0, sizeof(Matrix4x4) - 2 * sizeof(float)); + dst->M11 = dst->M22 = dst->M33 = dst->M44 = 1.0f; + return dst; + } + + INLINE void Mat4x4Ortho(float width, float height, float zNear, float zFar, Matrix4x4& result) + { + result.M12 = result.M13 = result.M14 = + result.M21 = result.M23 = result.M24 = + result.M31 = result.M32 = result.M34 = + result.M41 = result.M42 = 0.0f; + result.M44 = 1.0f; + + result.M11 = 2.0f / width; + result.M22 = 2.0f / height; + result.M33 = 1.0f / (zFar - zNear); + result.M43 = zNear / (zNear - zFar); + } + + INLINE Matrix4x4 Mat4x4Invert(const Matrix4x4* matrix) + { + Matrix4x4 matrix2; + float num5 = matrix->M11; + float num4 = matrix->M12; + float num3 = matrix->M13; + float num2 = matrix->M14; + float num9 = matrix->M21; + float num8 = matrix->M22; + float num7 = matrix->M23; + float num6 = matrix->M24; + float num17 = matrix->M31; + float num16 = matrix->M32; + float num15 = matrix->M33; + float num14 = matrix->M34; + float num13 = matrix->M41; + float num12 = matrix->M42; + float num11 = matrix->M43; + float num10 = matrix->M44; + float num23 = (num15 * num10) - (num14 * num11); + float num22 = (num16 * num10) - (num14 * num12); + float num21 = (num16 * num11) - (num15 * num12); + float num20 = (num17 * num10) - (num14 * num13); + float num19 = (num17 * num11) - (num15 * num13); + float num18 = (num17 * num12) - (num16 * num13); + float num39 = ((num8 * num23) - (num7 * num22)) + (num6 * num21); + float num38 = -(((num9 * num23) - (num7 * num20)) + (num6 * num19)); + float num37 = ((num9 * num22) - (num8 * num20)) + (num6 * num18); + float num36 = -(((num9 * num21) - (num8 * num19)) + (num7 * num18)); + float num = 1.0f / ((((num5 * num39) + (num4 * num38)) + (num3 * num37)) + (num2 * num36)); + matrix2.M11 = num39 * num; + matrix2.M21 = num38 * num; + matrix2.M31 = num37 * num; + matrix2.M41 = num36 * num; + matrix2.M12 = -(((num4 * num23) - (num3 * num22)) + (num2 * num21)) * num; + matrix2.M22 = (((num5 * num23) - (num3 * num20)) + (num2 * num19)) * num; + matrix2.M32 = -(((num5 * num22) - (num4 * num20)) + (num2 * num18)) * num; + matrix2.M42 = (((num5 * num21) - (num4 * num19)) + (num3 * num18)) * num; + float num35 = (num7 * num10) - (num6 * num11); + float num34 = (num8 * num10) - (num6 * num12); + float num33 = (num8 * num11) - (num7 * num12); + float num32 = (num9 * num10) - (num6 * num13); + float num31 = (num9 * num11) - (num7 * num13); + float num30 = (num9 * num12) - (num8 * num13); + matrix2.M13 = (((num4 * num35) - (num3 * num34)) + (num2 * num33)) * num; + matrix2.M23 = -(((num5 * num35) - (num3 * num32)) + (num2 * num31)) * num; + matrix2.M33 = (((num5 * num34) - (num4 * num32)) + (num2 * num30)) * num; + matrix2.M43 = -(((num5 * num33) - (num4 * num31)) + (num3 * num30)) * num; + float num29 = (num7 * num14) - (num6 * num15); + float num28 = (num8 * num14) - (num6 * num16); + float num27 = (num8 * num15) - (num7 * num16); + float num26 = (num9 * num14) - (num6 * num17); + float num25 = (num9 * num15) - (num7 * num17); + float num24 = (num9 * num16) - (num8 * num17); + matrix2.M14 = -(((num4 * num29) - (num3 * num28)) + (num2 * num27)) * num; + matrix2.M24 = (((num5 * num29) - (num3 * num26)) + (num2 * num25)) * num; + matrix2.M34 = -(((num5 * num28) - (num4 * num26)) + (num2 * num24)) * num; + matrix2.M44 = (((num5 * num27) - (num4 * num25)) + (num3 * num24)) * num; + return matrix2; + } + + INLINE void Mat4x4MulSSE(Matrix4x4* m1, Matrix4x4* m2, Matrix4x4* m3) + { + __m128 rowW = *((__m128*)(&m1->M11)); + __m128 rowX = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(rowW),_MM_SHUFFLE(0,0,0,0))); + __m128 rowY = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(rowW),_MM_SHUFFLE(1,1,1,1))); + __m128 rowZ = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(rowW),_MM_SHUFFLE(2,2,2,2))); + rowW = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(rowW),_MM_SHUFFLE(3,3,3,3))); + + rowX = _mm_mul_ps(rowX,*((__m128*)(&m2->M11))); + rowY = _mm_mul_ps(rowY,*((__m128*)(&m2->M21))); + rowZ = _mm_mul_ps(rowZ,*((__m128*)(&m2->M31))); + rowW = _mm_mul_ps(rowW,*((__m128*)(&m2->M41))); + rowX = _mm_add_ps(rowX,rowZ); + rowY = _mm_add_ps(rowY,rowW); + rowX = _mm_add_ps(rowX,rowY); + *((__m128*)(&m3->M11)) = rowX; + rowW = *((__m128*)(&m1->M21)); + + rowX = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(rowW),_MM_SHUFFLE(0,0,0,0))); + rowY = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(rowW),_MM_SHUFFLE(1,1,1,1))); + rowZ = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(rowW),_MM_SHUFFLE(2,2,2,2))); + rowW = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(rowW),_MM_SHUFFLE(3,3,3,3))); + + rowX = _mm_mul_ps(rowX,*((__m128*)(&m2->M11))); + rowY = _mm_mul_ps(rowY,*((__m128*)(&m2->M21))); + rowZ = _mm_mul_ps(rowZ,*((__m128*)(&m2->M31))); + rowW = _mm_mul_ps(rowW,*((__m128*)(&m2->M41))); + rowX = _mm_add_ps(rowX,rowZ); + rowY = _mm_add_ps(rowY,rowW); + rowX = _mm_add_ps(rowX,rowY); + *((__m128*)(&m3->M21)) = rowX; + rowW = *((__m128*)(&m1->M31)); + + rowX = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(rowW),_MM_SHUFFLE(0,0,0,0))); + rowY = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(rowW),_MM_SHUFFLE(1,1,1,1))); + rowZ = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(rowW),_MM_SHUFFLE(2,2,2,2))); + rowW = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(rowW),_MM_SHUFFLE(3,3,3,3))); + + rowX = _mm_mul_ps(rowX,*((__m128*)(&m2->M11))); + rowY = _mm_mul_ps(rowY,*((__m128*)(&m2->M21))); + rowZ = _mm_mul_ps(rowZ,*((__m128*)(&m2->M31))); + rowW = _mm_mul_ps(rowW,*((__m128*)(&m2->M41))); + rowX = _mm_add_ps(rowX,rowZ); + rowY = _mm_add_ps(rowY,rowW); + rowX = _mm_add_ps(rowX,rowY); + *((__m128*)(&m3->M31)) = rowX; + rowW = *((__m128*)(&m1->M41)); + + rowX = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(rowW),_MM_SHUFFLE(0,0,0,0))); + rowY = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(rowW),_MM_SHUFFLE(1,1,1,1))); + rowZ = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(rowW),_MM_SHUFFLE(2,2,2,2))); + rowW = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(rowW),_MM_SHUFFLE(3,3,3,3))); + + rowX = _mm_mul_ps(rowX,*((__m128*)(&m2->M11))); + rowY = _mm_mul_ps(rowY,*((__m128*)(&m2->M21))); + rowZ = _mm_mul_ps(rowZ,*((__m128*)(&m2->M31))); + rowW = _mm_mul_ps(rowW,*((__m128*)(&m2->M41))); + rowX = _mm_add_ps(rowX,rowZ); + rowY = _mm_add_ps(rowY,rowW); + rowX = _mm_add_ps(rowX,rowY); + *((__m128*)(&m3->M41)) = rowX; + } + + INLINE Matrix4x4* Mat4x4RotationAxis(Vec3f* axis, float angle, Matrix4x4* pResult) + { + float y = axis->Y; + float x = axis->X; + float z = axis->Z; + if (((float)(((x * x) + (y * y)) + (z * z))) != 1.0f) + { + Vec3Normalize(axis); + // axis.Normalize(); + } + float num4 = axis->X; + float num3 = axis->Y; + float num2 = axis->Z; + float num = cosf(angle); + float num5 = sinf(angle); + float num23 = num4; + float num17 = (num23 * num23); + float num22 = num3; + float num16 = (num22 * num22); + float num21 = num2; + float num15 = (num21 * num21); + float num20 = num3 * num4; + float num19 = num2 * num4; + float num18 = num2 * num3; + pResult->M11 = (((1.0f - num17) * num)) + num17; + float num14 = num20; + float num13 = num14 - (num * num14); + float num12 = num5 * num2; + pResult->M12 = (num12 + num13); + float num11 = num19; + float num10 = num11 - (num * num11); + float num9 = num5 * num3; + pResult->M13 = (num10 - num9); + pResult->M14 = 0.0f; + pResult->M21 = (num13 - num12); + pResult->M22 = (((1.0f - num16) * num)) + num16; + float num8 = num18; + float num7 = num8 - (num * num8); + float num6 = num5 * num4; + pResult->M23 = (float)(num6 + num7); + pResult->M24 = 0.0f; + pResult->M31 = (float)(num9 + num10); + pResult->M32 = (float)(num7 - num6); + pResult->M33 = ((float)((1.0f - num15) * num)) + num15; + pResult->M34 = 0.0f; + pResult->M41 = 0.0f; + pResult->M42 = 0.0f; + pResult->M43 = 0.0f; + pResult->M44 = 1.0f; + + return pResult; + } + + INLINE Matrix4x4 Mat4x4RotationX(float angle) + { + Matrix4x4 matrix; + float num2 = cosf(angle); + float num = sinf(angle); + matrix.M11 = 1.0f; + matrix.M12 = 0.0f; + matrix.M13 = 0.0f; + matrix.M14 = 0.0f; + matrix.M21 = 0.0f; + matrix.M22 = num2; + matrix.M23 = num; + matrix.M24 = 0.0f; + matrix.M31 = 0.0f; + matrix.M32 = -num; + matrix.M33 = num2; + matrix.M34 = 0.0f; + matrix.M41 = 0.0f; + matrix.M42 = 0.0f; + matrix.M43 = 0.0f; + matrix.M44 = 1.0f; + return matrix; + } + + INLINE void Mat4x4RotationX(float angle, Matrix4x4& matrix) + { + float num2 = cosf(angle); + float num = sinf(angle); + matrix.M11 = 1.0f; + matrix.M12 = 0.0f; + matrix.M13 = 0.0f; + matrix.M14 = 0.0f; + matrix.M21 = 0.0f; + matrix.M22 = num2; + matrix.M23 = num; + matrix.M24 = 0.0f; + matrix.M31 = 0.0f; + matrix.M32 = -num; + matrix.M33 = num2; + matrix.M34 = 0.0f; + matrix.M41 = 0.0f; + matrix.M42 = 0.0f; + matrix.M43 = 0.0f; + matrix.M44 = 1.0f; + } + + INLINE Matrix4x4 Mat4x4RotationY(float angle) + { + Matrix4x4 matrix; + float num2 = cosf(angle); + float num = sinf(angle); + matrix.M11 = num2; + matrix.M12 = 0.0f; + matrix.M13 = -num; + matrix.M14 = 0.0f; + matrix.M21 = 0.0f; + matrix.M22 = 1.0f; + matrix.M23 = 0.0f; + matrix.M24 = 0.0f; + matrix.M31 = num; + matrix.M32 = 0.0f; + matrix.M33 = num2; + matrix.M34 = 0.0f; + matrix.M41 = 0.0f; + matrix.M42 = 0.0f; + matrix.M43 = 0.0f; + matrix.M44 = 1.0f; + return matrix; + } + + INLINE void Mat4x4RotationY(float angle, Matrix4x4& matrix) + { + float num2 = cosf(angle); + float num = sinf(angle); + matrix.M11 = num2; + matrix.M12 = 0.0f; + matrix.M13 = -num; + matrix.M14 = 0.0f; + matrix.M21 = 0.0f; + matrix.M22 = 1.0f; + matrix.M23 = 0.0f; + matrix.M24 = 0.0f; + matrix.M31 = num; + matrix.M32 = 0.0f; + matrix.M33 = num2; + matrix.M34 = 0.0f; + matrix.M41 = 0.0f; + matrix.M42 = 0.0f; + matrix.M43 = 0.0f; + matrix.M44 = 1.0f; + } + + INLINE Matrix4x4 Mat4x4RotationZ(float angle) + { + Matrix4x4 matrix; + float num2 = cosf(angle); + float num = sinf(angle); + matrix.M11 = num2; + matrix.M12 = num; + matrix.M13 = 0.0f; + matrix.M14 = 0.0f; + matrix.M21 = -num; + matrix.M22 = num2; + matrix.M23 = 0.0f; + matrix.M24 = 0.0f; + matrix.M31 = 0.0f; + matrix.M32 = 0.0f; + matrix.M33 = 1.0f; + matrix.M34 = 0.0f; + matrix.M41 = 0.0f; + matrix.M42 = 0.0f; + matrix.M43 = 0.0f; + matrix.M44 = 1.0f; + return matrix; + } + + INLINE void Mat4x4RotationZ(float angle, Matrix4x4& matrix) + { + float num2 = cosf(angle); + float num = sinf(angle); + matrix.M11 = num2; + matrix.M12 = num; + matrix.M13 = 0.0f; + matrix.M14 = 0.0f; + matrix.M21 = -num; + matrix.M22 = num2; + matrix.M23 = 0.0f; + matrix.M24 = 0.0f; + matrix.M31 = 0.0f; + matrix.M32 = 0.0f; + matrix.M33 = 1.0f; + matrix.M34 = 0.0f; + matrix.M41 = 0.0f; + matrix.M42 = 0.0f; + matrix.M43 = 0.0f; + matrix.M44 = 1.0f; + } + + INLINE void Mat4x4RotationQuaternion(const Quaternion& rotation, Matrix4x4& result) + { + float x = rotation.X; + float num9 = (x * x); + float y = rotation.Y; + float num8 = (y * y); + float z = rotation.Z; + float num7 = (z * z); + float num6 = rotation.Y * rotation.X; + float num5 = rotation.W * rotation.Z; + float num4 = rotation.Z * rotation.X; + float num3 = rotation.W * rotation.Y; + float num2 = rotation.Z * rotation.Y; + float num = rotation.W * rotation.X; + result.M11 = (1.0f - ((num7 + num8) * 2.0f)); + result.M12 = ((num5 + num6) * 2.0f); + result.M13 = ((num4 - num3) * 2.0f); + result.M14 = 0.0f; + result.M21 = ((num6 - num5) * 2.0f); + result.M22 = (1.0f - ((num7 + num9) * 2.0f)); + result.M23 = ((num + num2) * 2.0f); + result.M24 = 0.0f; + result.M31 = ((num3 + num4) * 2.0f); + result.M32 = ((num2 - num) * 2.0f); + result.M33 = (1.0f - ((num8 + num9) * 2.0f)); + result.M34 = 0.0f; + result.M41 = 0.0f; + result.M42 = 0.0f; + result.M43 = 0.0f; + result.M44 = 1.0f; + } + + INLINE void Mat4x4RotationYawPitchRoll(float yaw, float pitch, float roll, Matrix4x4& out) + { + Quaternion quaternion; + QuatRotationYawPitchRoll(yaw, pitch, roll, quaternion); + Mat4x4RotationQuaternion(quaternion, out); + } + + INLINE void Mat4x4Scaling(float xScale, float yScale, float zScale, Matrix4x4& out) + { + out.M11 = xScale; + out.M12 = 0.0f; + out.M13 = 0.0f; + out.M14 = 0.0f; + out.M21 = 0.0f; + out.M22 = yScale; + out.M23 = 0.0f; + out.M24 = 0.0f; + out.M31 = 0.0f; + out.M32 = 0.0f; + out.M33 = zScale; + out.M34 = 0.0f; + out.M41 = 0.0f; + out.M42 = 0.0f; + out.M43 = 0.0f; + out.M44 = 1.0f; + } + + INLINE void Mat4x4Scaling(Vec3f& amt, Matrix4x4& matrix) + { + matrix.M11 = amt.X; + matrix.M12 = 0.0f; + matrix.M13 = 0.0f; + matrix.M14 = 0.0f; + matrix.M21 = 0.0f; + matrix.M22 = amt.Y; + matrix.M23 = 0.0f; + matrix.M24 = 0.0f; + matrix.M31 = 0.0f; + matrix.M32 = 0.0f; + matrix.M33 = amt.Z; + matrix.M34 = 0.0f; + matrix.M41 = 0.0f; + matrix.M42 = 0.0f; + matrix.M43 = 0.0f; + matrix.M44 = 1.0f; + } + + INLINE void Mat4x4Translation(const Vec3f& amount, Matrix4x4& matrix) + { + matrix.M11 = 1.0f; + matrix.M12 = 0.0f; + matrix.M13 = 0.0f; + matrix.M14 = 0.0f; + matrix.M21 = 0.0f; + matrix.M22 = 1.0f; + matrix.M23 = 0.0f; + matrix.M24 = 0.0f; + matrix.M31 = 0.0f; + matrix.M32 = 0.0f; + matrix.M33 = 1.0f; + matrix.M34 = 0.0f; + matrix.M41 = amount.X; + matrix.M42 = amount.Y; + matrix.M43 = amount.Z; + matrix.M44 = 1.0f; + } + + INLINE void Mat4x4Translation(float _x, float _y, float _z, Matrix4x4& matrix) + { + matrix.M11 = 1.0f; + matrix.M12 = 0.0f; + matrix.M13 = 0.0f; + matrix.M14 = 0.0f; + matrix.M21 = 0.0f; + matrix.M22 = 1.0f; + matrix.M23 = 0.0f; + matrix.M24 = 0.0f; + matrix.M31 = 0.0f; + matrix.M32 = 0.0f; + matrix.M33 = 1.0f; + matrix.M34 = 0.0f; + matrix.M41 = _x; + matrix.M42 = _y; + matrix.M43 = _z; + matrix.M44 = 1.0f; + } + + INLINE void Mat4x4Transpose(const Matrix4x4& src, Matrix4x4& dst) + { + Matrix4x4 tmp = src; + dst.M11 = tmp.M11; + dst.M12 = tmp.M21; + dst.M13 = tmp.M31; + dst.M14 = tmp.M41; + dst.M21 = tmp.M12; + dst.M22 = tmp.M22; + dst.M23 = tmp.M32; + dst.M24 = tmp.M42; + dst.M31 = tmp.M13; + dst.M32 = tmp.M23; + dst.M33 = tmp.M33; + dst.M34 = tmp.M43; + dst.M41 = tmp.M14; + dst.M42 = tmp.M24; + dst.M43 = tmp.M34; + dst.M44 = tmp.M44; + } + + INLINE void Mat4x4Lerp(const Matrix4x4& start, const Matrix4x4& end, Matrix4x4& result, float amount) + { + result.M11 = ((end.M11 - start.M11) * amount) + start.M11; + result.M12 = ((end.M12 - start.M12) * amount) + start.M12; + result.M13 = ((end.M13 - start.M13) * amount) + start.M13; + result.M14 = ((end.M14 - start.M14) * amount) + start.M14; + result.M21 = ((end.M21 - start.M21) * amount) + start.M21; + result.M22 = ((end.M22 - start.M22) * amount) + start.M22; + result.M23 = ((end.M23 - start.M23) * amount) + start.M23; + result.M24 = ((end.M24 - start.M24) * amount) + start.M24; + result.M31 = ((end.M31 - start.M31) * amount) + start.M31; + result.M32 = ((end.M32 - start.M32) * amount) + start.M32; + result.M33 = ((end.M33 - start.M33) * amount) + start.M33; + result.M34 = ((end.M34 - start.M34) * amount) + start.M34; + result.M41 = ((end.M41 - start.M41) * amount) + start.M41; + result.M42 = ((end.M42 - start.M42) * amount) + start.M42; + result.M43 = ((end.M43 - start.M43) * amount) + start.M43; + result.M44 = ((end.M44 - start.M44) * amount) + start.M44; + } + + INLINE void Mat4x4Billboard(const Vec3f& objectPosition, const Vec3f& cameraPosition, const Vec3f& cameraUpVector, const Vec3f& cameraForwardVector, Matrix4x4& matrix) + { + Vec3f eyeDir = objectPosition - cameraPosition; + Vec3f right = eyeDir; + Vec3f result; + Vec3f vector3; + + float x = right.X; + float y = right.Y; + float z = right.Z; + + float num = (((x * x) + (y * y)) + (z * z)); + if (num < 9.99999974737875f) + { + right = -(cameraForwardVector); + } + else + { + right = (Vec3f)(eyeDir * ((1.0f / sqrtf(num)))); + } + Vec3Cross(cameraUpVector, right, result); + Vec3Normalize(&result); + Vec3Cross(right, result, vector3); + + matrix.M11 = vector3.X; + matrix.M12 = vector3.Y; + matrix.M13 = vector3.Z; + matrix.M14 = 0.0f; + matrix.M21 = result.X; + matrix.M22 = result.Y; + matrix.M23 = result.Z; + matrix.M24 = 0.0f; + matrix.M31 = right.X; + matrix.M32 = right.Y; + matrix.M33 = right.Z; + matrix.M34 = 0.0f; + matrix.M41 = objectPosition.X; + matrix.M42 = objectPosition.Y; + matrix.M43 = objectPosition.Z; + matrix.M44 = 1.0f; + } + + INLINE void Mat4x4LookAtLH(const Vec3f& cameraPosition, const Vec3f& cameraTarget, const Vec3f& cameraUpVector, Matrix4x4& matrix) + { + Vec3f camDir = cameraTarget - cameraPosition; + Vec3Normalize(&camDir); + + // Vec3f right = Vector3D.Normalize(Vector3D.Cross(cameraUpVector, camDir)); + + Vec3f right; + Vec3Cross(cameraUpVector, camDir, right); + Vec3Normalize(&right); + + Vec3f up; + Vec3Cross(camDir, right, up); + + matrix.M11 = right.X; + matrix.M12 = up.X; + matrix.M13 = camDir.X; + matrix.M14 = 0.0f; + matrix.M21 = right.Y; + matrix.M22 = up.Y; + matrix.M23 = camDir.Y; + matrix.M24 = 0.0f; + matrix.M31 = right.Z; + matrix.M32 = up.Z; + matrix.M33 = camDir.Z; + matrix.M34 = 0.0f; + matrix.M41 = -Vec3Dot(&right, &cameraPosition); + matrix.M42 = -Vec3Dot(&up, &cameraPosition); + matrix.M43 = -Vec3Dot(&camDir, &cameraPosition); + matrix.M44 = 1.0f; + } + + INLINE void Mat4x4Decompose(Matrix4x4& matrix, Vec3f& position, Vec3f& scale, Quaternion& rot) + { + //compute scaling + Vec3f Row0(matrix.M11, matrix.M12, matrix.M13); + Vec3f Row1(matrix.M21, matrix.M22, matrix.M23); + Vec3f Row2(matrix.M31, matrix.M32, matrix.M33); + scale.X = Vec3Length(&Row0); + scale.Y = Vec3Length(&Row1); + scale.Z = Vec3Length(&Row2); + + //translation + position.X = matrix.M41; + position.Y = matrix.M42; + position.Z = matrix.M43; + + //rotation + QuatRotationMatrix(matrix,rot); + } + + INLINE void Mat4x4PerspectiveFOV(float fieldOfView, float aspectRatio, float nearPlaneDistance, float farPlaneDistance, Matrix4x4& matrix) + { + /*if ((fieldOfView <= 0.0f) || (fieldOfView >= (2.0f * 3.1415926535f))) + { + throw new ArgumentOutOfRangeException("FieldOfView is not correct!"); + } + if (nearPlaneDistance <= 0.0f) + { + throw new ArgumentOutOfRangeException("Near plane distance must be greater than zero"); + } + if (farPlaneDistance <= 0.0f) + { + throw new ArgumentOutOfRangeException("Far plane distance must be greater than zero"); + } + if (nearPlaneDistance >= farPlaneDistance) + { + throw new ArgumentOutOfRangeException("Distance to the far plane must be greater than distance to the near plane!"); + }*/ + float num = 1.0f / (tanf(fieldOfView * 0.5f)); + float num9 = num / aspectRatio; + matrix.M11 = num9; + matrix.M12 = matrix.M13 = matrix.M14 = 0.0f; + matrix.M22 = num; + matrix.M21 = matrix.M23 = matrix.M24 = 0.0f; + matrix.M31 = matrix.M32 = 0.0f; + matrix.M33 = farPlaneDistance / (farPlaneDistance - nearPlaneDistance); + matrix.M34 = 1.0f; + matrix.M41 = matrix.M42 = matrix.M44 = 0.0f; + matrix.M43 = -nearPlaneDistance * farPlaneDistance / (farPlaneDistance - nearPlaneDistance); + } + //-----Quaternion----- + INLINE void QuatIdentity(Quaternion& pDst) + { + pDst.W = pDst.Y = pDst.Z = 0.0f; + pDst.X = 1.0f; + } + + INLINE void QuatLerp(const Quaternion& quaternion1, const Quaternion& quaternion2, float amount, Quaternion& result) + { + float num = amount; + float num2 = 1.0f - num; + float num5 = (((quaternion1.X * quaternion2.X) + (quaternion1.Y * quaternion2.Y)) + (quaternion1.Z * quaternion2.Z)) + (quaternion1.W * quaternion2.W); + if (num5 >= 0.0f) + { + result.X = (num2 * quaternion1.X) + (num * quaternion2.X); + result.Y = (num2 * quaternion1.Y) + (num * quaternion2.Y); + result.Z = (num2 * quaternion1.Z) + (num * quaternion2.Z); + result.W = (num2 * quaternion1.W) + (num * quaternion2.W); + } + else + { + result.X = (num2 * quaternion1.X) - (num * quaternion2.X); + result.Y = (num2 * quaternion1.Y) - (num * quaternion2.Y); + result.Z = (num2 * quaternion1.Z) - (num * quaternion2.Z); + result.W = (num2 * quaternion1.W) - (num * quaternion2.W); + } + float num4 = (((result.X * result.X) + (result.Y * result.Y)) + (result.Z * result.Z)) + (result.W * result.W); + + if(num4 <=0) + printf("OLOLOOOOOOOOOOOOOOOOO"); + float num3 = 1.0f / sqrtf(num4); + result.X *= num3; + result.Y *= num3; + result.Z *= num3; + result.W *= num3; + } + + INLINE void QuatSLerp( const Quaternion &from, const Quaternion &to, float t, Quaternion& result ) + { + Quaternion temp; + float omega, cosom, sinom, scale0, scale1; + + if ( t <= 0.0f ) + { + result = from; + return; + } + + if ( t >= 1.0f ) + { + result = to; + return; + } + + if ( from == to ) + { + result = to; + return; + } + + cosom = from.X * to.X + from.Y * to.Y + from.Z * to.Z + from.W * to.W; + if ( cosom < 0.0f ) + { + temp = -to; + cosom = -cosom; + } else + { + temp = to; + } + + if ( ( 1.0f - cosom ) > 1e-6f ) { + omega = acos( cosom ); + sinom = 1.0f / sin( omega ); + scale0 = sin( ( 1.0f - t ) * omega ) * sinom; + scale1 = sin( t * omega ) * sinom; + } else { + scale0 = 1.0f - t; + scale1 = t; + } + + result = ( from * scale0) + ( temp * scale1); + } + + INLINE float QuatLength(const Quaternion& src) + { + return sqrtf((src.X * src.X) + (src.Y * src.Y) + (src.Z * src.Z) + (src.W * src.W)); + } + + INLINE void QuatNormalize(Quaternion& src) + { + float num = (1.0f / QuatLength(src)); + src.X *= num; + src.Y *= num; + src.Z *= num; + src.W *= num; + } + + INLINE void QuatSetAxisAngle(const Vec3f& axis, float theta, Quaternion& dst) + { + dst.X = sinf(theta * 0.5f) * axis.X; + dst.Y = sinf(theta * 0.5f) * axis.Y; + dst.Z = sinf(theta * 0.5f) * axis.Z; + dst.W = cosf(theta * 0.5f); + } + + INLINE void QuatInvert(Quaternion& src) + { + float num2 = (((src.X * src.X) + (src.Y * src.Y)) + (src.Z * src.Z)) + (src.W * src.W); + float num = 1.0f / num2; + src.X = -src.X * num; + src.Y = -src.Y * num; + src.Z = -src.Z * num; + src.W = src.W * num; + } + //-----Plane----- + INLINE bool PlaneIntersectLine(const Plane& plane, const Vec3f& start, const Vec3f& dir, float& dist) + { + dist = 0; + float num2 = ((plane.Normal.X * dir.X) + (plane.Normal.Y * dir.Y)) + (plane.Normal.Z * dir.Z); + if (fabs(num2) < 1E-05f) + { + return false; + } + float num3 = ((plane.Normal.X * start.X) + (plane.Normal.Y * start.Y)) + (plane.Normal.Z * start.Z); + float num = (-plane.D - num3) / num2; + if (num < 0.0f) + { + if (num < -1E-05f) + { + return false; + } + num = 0.0f; + } + dist = num; + return true; + } + INLINE void PlaneNormalize(Plane& plane) + { + float num2 = ((plane.Normal.X * plane.Normal.X) + (plane.Normal.Y * plane.Normal.Y)) + (plane.Normal.Z * plane.Normal.Z); + if (fabs(num2 - 1.0f) >= 1.192093E-07f) + { + float num = 1.0f / sqrtf(num2); + plane.Normal.X *= num; + plane.Normal.Y *= num; + plane.Normal.Z *= num; + plane.D *= num; + } + } + INLINE void PlaneTransform(Plane& plane, const Matrix4x4& matrix) + { + Matrix4x4 matrix2 = matrix; + matrix2 = Mat4x4Invert(&matrix2); + float x = plane.Normal.X; + float y = plane.Normal.Y; + float z = plane.Normal.Z; + float d = plane.D; + plane.Normal.X = (((x * matrix2.M11) + (y * matrix2.M12)) + (z * matrix2.M13)) + (d * matrix2.M14); + plane.Normal.Y = (((x * matrix2.M21) + (y * matrix2.M22)) + (z * matrix2.M23)) + (d * matrix2.M24); + plane.Normal.Z = (((x * matrix2.M31) + (y * matrix2.M32)) + (z * matrix2.M33)) + (d * matrix2.M34); + plane.D = (((x * matrix2.M41) + (y * matrix2.M42)) + (z * matrix2.M43)) + (d * matrix2.M44); + } + INLINE void PlaneTransform(const Plane& plane, const Matrix4x4& matrix, Plane& result) + { + Matrix4x4 matrix2 = matrix; + matrix2 = Mat4x4Invert(&matrix2); + float x = plane.Normal.X; + float y = plane.Normal.Y; + float z = plane.Normal.Z; + float d = plane.D; + result.Normal.X = (((x * matrix2.M11) + (y * matrix2.M12)) + (z * matrix2.M13)) + (d * matrix2.M14); + result.Normal.Y = (((x * matrix2.M21) + (y * matrix2.M22)) + (z * matrix2.M23)) + (d * matrix2.M24); + result.Normal.Z = (((x * matrix2.M31) + (y * matrix2.M32)) + (z * matrix2.M33)) + (d * matrix2.M34); + result.D = (((x * matrix2.M41) + (y * matrix2.M42)) + (z * matrix2.M43)) + (d * matrix2.M44); + } + INLINE float PlaneDot(const Plane& plane, const Vec4f& value) + { + return ((((plane.Normal.X * value.X) + (plane.Normal.Y * value.Y)) + (plane.Normal.Z * value.Z)) + (plane.D * value.W)); + } + INLINE float PlaneDotCoordinate(const Plane& plane, const Vec3f& value) + { + return ((((plane.Normal.X * value.X) + (plane.Normal.Y * value.Y)) + (plane.Normal.Z * value.Z)) + plane.D); + } + + INLINE float PlaneDotNormal(const Plane& plane, const Vec3f& value) + { + return (((plane.Normal.X * value.X) + (plane.Normal.Y * value.Y)) + (plane.Normal.Z * value.Z)); + } + INLINE float PlaneDot(const Plane& plane, const Vec3f& point) + { + return ((((plane.Normal.Y * point.Y) + (plane.Normal.X * point.X)) + (plane.Normal.Z * point.Z)) + plane.D); + } + + INLINE void Get4x4Offsets(int destTextureWidth, int destTextureHeight, Vec4f* output) + { + float tu = 1.0f / destTextureWidth; + float tv = 1.0f / destTextureHeight; + + int index = 0; + for (int x = 0; x < 4; x++) + { + for (int y = 0; y < 4; y++) + { + output[index].X = (x - 1.5f) * tu; + output[index].Y = (y - 1.5f) * tv; + + index++; + } + } + } + INLINE void Get3x3Offsets(int destTextureWidth, int destTextureHeight, Vec4f* output) + { + float sampleOffset = 1.0f; + float tu = 1.0f / destTextureWidth * sampleOffset; + float tv = 1.0f / destTextureHeight * sampleOffset; + + int index = 0; + for (int x = 0; x < 3; x++) + { + for (int y = 0; y < 3; y++) + { + output[index].X = (x - 1.5f) * tu; + output[index].Y = (y - 1.5f) * tv; + + index++; + } + } + } + INLINE void Get2x2Offsets(int destTextureWidth, int destTextureHeight, Vec4f* output) + { + float sampleOffset = 1.0f; + float tu = 1.0f / destTextureWidth * sampleOffset; + float tv = 1.0f / destTextureHeight * sampleOffset; + + //Vector4D[] offsets = new Vector4D[4]; + int index = 0; + for (int x = 0; x < 2; x++) + { + for (int y = 0; y < 2; y++) + { + output[index].X = (x - 0.5f) * tu; + output[index].Y = (y - 0.5f) * tv; + + index++; + } + } + } + INLINE void GetBlurWeightedOffsets(float numSamples, int destTextureDimensionLength, Vec4f* output) + { + float tuv = 1.0f / destTextureDimensionLength; + + float offset = -(numSamples - 1.0f) / 2.0f; + for (int i = 0; i < numSamples; i++) + { + output[i].X = offset * tuv; + offset++; + float x = 2.0f * i / (numSamples - 1.0f) - 1.0f; + output[i].Y = -0.25f * x * x + 0.225f; + } + } +} +#endif // LifeMath_h__ \ No newline at end of file diff --git a/LifeCore/math/Matrix4x4.h b/LifeCore/math/Matrix4x4.h new file mode 100644 index 0000000..ab952e6 --- /dev/null +++ b/LifeCore/math/Matrix4x4.h @@ -0,0 +1,274 @@ +#ifndef __MATRIX4X4__ +#define __MATRIX4X4__ +#include "Math.h" + +namespace LifeMath +{ +#undef INLINE +#ifdef _WIN32 +#ifdef _MSC_VER +#define INLINE __forceinline +#define ALIGN_MATRIX __declspec(align(16)) +#endif +#else +#define ALIGN_MATRIX +#ifdef __GNUC__ +#define INLINE __attribute__((always_inline)) +#endif +#endif +#ifndef INLINE +#define INLINE inline +#endif + + typedef ALIGN_MATRIX struct Matrix4x4 + { + public: + float M11; + float M12; + float M13; + float M14; + float M21; + float M22; + float M23; + float M24; + float M31; + float M32; + float M33; + float M34; + float M41; + float M42; + float M43; + float M44; + + void setup( + float _m11, float _m12, float _m13, float _m14, + float _m21, float _m22, float _m23, float _m24, + float _m31, float _m32, float _m33, float _m34, + float _m41, float _m42, float _m43, float _m44) + { + M11 = _m11; + M12 = _m12; + M13 = _m13; + M14 = _m14; + + M21 = _m21; + M22 = _m22; + M23 = _m23; + M24 = _m24; + + M31 = _m31; + M32 = _m32; + M33 = _m33; + M34 = _m34; + + M41 = _m41; + M42 = _m42; + M43 = _m43; + M44 = _m44; + } + + INLINE Matrix4x4 operator *(const Matrix4x4& matrix2) const + { + Matrix4x4 matrix; + matrix.M11 = (((this->M11 * matrix2.M11) + (this->M12 * matrix2.M21)) + (this->M13 * matrix2.M31)) + (this->M14 * matrix2.M41); + matrix.M12 = (((this->M11 * matrix2.M12) + (this->M12 * matrix2.M22)) + (this->M13 * matrix2.M32)) + (this->M14 * matrix2.M42); + matrix.M13 = (((this->M11 * matrix2.M13) + (this->M12 * matrix2.M23)) + (this->M13 * matrix2.M33)) + (this->M14 * matrix2.M43); + matrix.M14 = (((this->M11 * matrix2.M14) + (this->M12 * matrix2.M24)) + (this->M13 * matrix2.M34)) + (this->M14 * matrix2.M44); + matrix.M21 = (((this->M21 * matrix2.M11) + (this->M22 * matrix2.M21)) + (this->M23 * matrix2.M31)) + (this->M24 * matrix2.M41); + matrix.M22 = (((this->M21 * matrix2.M12) + (this->M22 * matrix2.M22)) + (this->M23 * matrix2.M32)) + (this->M24 * matrix2.M42); + matrix.M23 = (((this->M21 * matrix2.M13) + (this->M22 * matrix2.M23)) + (this->M23 * matrix2.M33)) + (this->M24 * matrix2.M43); + matrix.M24 = (((this->M21 * matrix2.M14) + (this->M22 * matrix2.M24)) + (this->M23 * matrix2.M34)) + (this->M24 * matrix2.M44); + matrix.M31 = (((this->M31 * matrix2.M11) + (this->M32 * matrix2.M21)) + (this->M33 * matrix2.M31)) + (this->M34 * matrix2.M41); + matrix.M32 = (((this->M31 * matrix2.M12) + (this->M32 * matrix2.M22)) + (this->M33 * matrix2.M32)) + (this->M34 * matrix2.M42); + matrix.M33 = (((this->M31 * matrix2.M13) + (this->M32 * matrix2.M23)) + (this->M33 * matrix2.M33)) + (this->M34 * matrix2.M43); + matrix.M34 = (((this->M31 * matrix2.M14) + (this->M32 * matrix2.M24)) + (this->M33 * matrix2.M34)) + (this->M34 * matrix2.M44); + matrix.M41 = (((this->M41 * matrix2.M11) + (this->M42 * matrix2.M21)) + (this->M43 * matrix2.M31)) + (this->M44 * matrix2.M41); + matrix.M42 = (((this->M41 * matrix2.M12) + (this->M42 * matrix2.M22)) + (this->M43 * matrix2.M32)) + (this->M44 * matrix2.M42); + matrix.M43 = (((this->M41 * matrix2.M13) + (this->M42 * matrix2.M23)) + (this->M43 * matrix2.M33)) + (this->M44 * matrix2.M43); + matrix.M44 = (((this->M41 * matrix2.M14) + (this->M42 * matrix2.M24)) + (this->M43 * matrix2.M34)) + (this->M44 * matrix2.M44); + return matrix; + } + + /* public MatrixBasis Basis + { + get + { + MatrixBasis basis = new MatrixBasis(); + basis.M11 = this.M11; + basis.M12 = this.M12; + basis.M13 = this.M13; + + basis.M21 = this.M21; + basis.M22 = this.M22; + basis.M23 = this.M23; + + basis.M31 = this.M31; + basis.M32 = this.M32; + basis.M33 = this.M33; + return basis; + } + set + { + this.M11 = value.M11; + this.M12 = value.M12; + this.M13 = value.M13; + + this.M21 = value.M21; + this.M22 = value.M22; + this.M23 = value.M23; + + this.M31 = value.M31; + this.M32 = value.M32; + this.M33 = value.M33; + } + }*/ + + /* + + #region Decomposition + public Vec3f Up + { + get + { + Vec3f vector; + vector.X = this.M21; + vector.Y = this.M22; + vector.Z = this.M23; + return vector; + } + set + { + this.M21 = value.X; + this.M22 = value.Y; + this.M23 = value.Z; + } + } + + public Vec3f Down + { + get + { + Vec3f vector; + vector.X = -this.M21; + vector.Y = -this.M22; + vector.Z = -this.M23; + return vector; + } + set + { + this.M21 = -value.X; + this.M22 = -value.Y; + this.M23 = -value.Z; + } + } + + public Vec3f Right + { + get + { + Vec3f vector; + vector.X = this.M11; + vector.Y = this.M12; + vector.Z = this.M13; + return vector; + } + set + { + this.M11 = value.X; + this.M12 = value.Y; + this.M13 = value.Z; + } + } + + public Vec3f Left + { + get + { + Vec3f vector; + vector.X = -this.M11; + vector.Y = -this.M12; + vector.Z = -this.M13; + return vector; + } + set + { + this.M11 = -value.X; + this.M12 = -value.Y; + this.M13 = -value.Z; + } + } + + public Vec3f Forward + { + get + { + Vec3f vector; + vector.X = -this.M31; + vector.Y = -this.M32; + vector.Z = -this.M33; + return vector; + } + set + { + this.M31 = -value.X; + this.M32 = -value.Y; + this.M33 = -value.Z; + } + } + + public Vec3f Backward + { + get + { + Vec3f vector; + vector.X = this.M31; + vector.Y = this.M32; + vector.Z = this.M33; + return vector; + } + set + { + this.M31 = value.X; + this.M32 = value.Y; + this.M33 = value.Z; + } + } + + public Vec3f Position + { + get + { + Vec3f vector; + vector.X = this.M41; + vector.Y = this.M42; + vector.Z = this.M43; + return vector; + } + set + { + this.M41 = value.X; + this.M42 = value.Y; + this.M43 = value.Z; + } + } + + public void Decompose(out Vec3f scale, out CQuaternion rotation, out Vec3f translation) + { + MatrixBasis b = this.Basis; + + scale = b.Scaling; + + translation.X = this.M41; + translation.Y = this.M42; + translation.Z = this.M43; + + b.Scaling = new Vec3f(1, 1, 1); + + rotation = CQuaternion.RotationBasis(b); + } + + #endregion*/ + } Matrix4x4, *PMatrix4x4, **LPMatrix4x4; + typedef Matrix4x4 float4x4; +} +#endif \ No newline at end of file diff --git a/LifeCore/math/Plane.h b/LifeCore/math/Plane.h new file mode 100644 index 0000000..e2c76f3 --- /dev/null +++ b/LifeCore/math/Plane.h @@ -0,0 +1,96 @@ +#ifndef Plane_h__ +#define Plane_h__ +#include "Vector3D.h" +#include "Vector4D.h" +#include + +namespace LifeMath +{ +#undef INLINE +#ifdef _WIN32 +#ifdef _MSC_VER +#define INLINE __forceinline +#endif +#else +#ifdef __GNUC__ +#define INLINE __attribute__((always_inline)) +#endif +#endif + +#ifndef INLINE +#define INLINE inline +#endif + + class Plane + { + public: + Vec3f Normal; + float D; + Plane(float a, float b, float c, float d) + { + Normal.X = a; + Normal.Y = b; + Normal.Z = c; + D = d; + } + Plane() + { + Normal.X = 0.0f; + Normal.Y = 1.0f; + Normal.Z = 0.0f; + D = 0.0f; + } + + Plane(const Vec3f& _normal, float d) + { + Normal = _normal; + D = d; + } + + Plane(const Vec3f& point, const Vec3f& normal) + { + Normal = normal; + D = -(((normal.Y * point.Y) + (normal.X * point.X)) + (normal.Z * point.Z)); + } + + Plane(const Vec4f& val) + { + Normal.X = val.X; + Normal.Y = val.Y; + Normal.Z = val.Z; + D = val.W; + } + Plane(Vec3f& point1, Vec3f& point2, Vec3f& point3) + { + float num10 = point2.X - point1.X; + float num9 = point2.Y - point1.Y; + float num8 = point2.Z - point1.Z; + float num7 = point3.X - point1.X; + float num6 = point3.Y - point1.Y; + float num5 = point3.Z - point1.Z; + float num4 = (num9 * num5) - (num8 * num6); + float num3 = (num8 * num7) - (num10 * num5); + float num2 = (num10 * num6) - (num9 * num7); + float num11 = ((num4 * num4) + (num3 * num3)) + (num2 * num2); + float num = 1.0f / sqrtf(num11); + Normal.X = num4 * num; + Normal.Y = num3 * num; + Normal.Z = num2 * num; + D = -(((Normal.X * point1.X) + (Normal.Y * point1.Y)) + (Normal.Z * point1.Z)); + } + INLINE bool operator ==(const Plane& other) const + { + return (other.Normal == this->Normal && other.D == this->D); + } + + INLINE bool operator !=(const Plane& other) const + { + if(this->Normal == other.Normal) + { + return (this->D != other.D); + } + return true; + } + }; +} +#endif // Plane_h__ \ No newline at end of file diff --git a/LifeCore/math/Quaternion.h b/LifeCore/math/Quaternion.h new file mode 100644 index 0000000..833e495 --- /dev/null +++ b/LifeCore/math/Quaternion.h @@ -0,0 +1,93 @@ +#ifndef __QUATERNION__ +#define __QUATERNION__ +#include "Math.h" +#include + +namespace LifeMath +{ +#undef INLINE +#ifdef _WIN32 +#ifdef _MSC_VER +#define INLINE __forceinline +#endif +#else +#ifdef __GNUC__ +#define INLINE __attribute__((always_inline)) +#endif +#endif + +#ifndef INLINE +#define INLINE inline +#endif + + struct Quaternion + { + public: + float X,Y,Z,W; + INLINE Quaternion() + { + X = Y = Z = W = 0.0f; + } + + INLINE Quaternion operator -() const + { + return Quaternion(-X, -Y, -Z, -W); + } + + INLINE bool operator == (const Quaternion& right) const + { + return (X == right.X && Y == right.Y && Z == right.Z && W == right.W); + } + + INLINE bool operator != (const Quaternion& right) const + { + return (X != right.X || Y != right.Y || Z != right.Z || W != right.W); + } + + INLINE Quaternion(float x, float y, float z, float w) + { + X = x; + Y = y; + Z = z; + W = w; + } + INLINE Quaternion& operator =(const Quaternion& other) + { + X = other.X; + Y = other.Y; + Z = other.Z; + W = other.W; + return *this; + } + INLINE Quaternion operator *(float other) const + { + return Quaternion(X * other, Y * other, Z * other, W * other); + } + INLINE Quaternion operator +(const Quaternion& other) const + { + return Quaternion(X + other.X, Y + other.Y, Z + other.Z, W + other.W); + } + INLINE Quaternion operator *(const Quaternion& other) const + { + Quaternion quaternion; + float x = this->X; + float y = this->Y; + float z = this->Z; + float w = this->W; + float num4 = other.X; + float num3 = other.Y; + float num2 = other.Z; + float num = other.W; + float num12 = (y * num2) - (z * num3); + float num11 = (z * num4) - (x * num2); + float num10 = (x * num3) - (y * num4); + float num9 = ((x * num4) + (y * num3)) + (z * num2); + quaternion.X = ((x * num) + (num4 * w)) + num12; + quaternion.Y = ((y * num) + (num3 * w)) + num11; + quaternion.Z = ((z * num) + (num2 * w)) + num10; + quaternion.W = (w * num) - num9; + return quaternion; + } + }; +} +#endif \ No newline at end of file diff --git a/LifeCore/math/Rectangle.h b/LifeCore/math/Rectangle.h new file mode 100644 index 0000000..81cf2cd --- /dev/null +++ b/LifeCore/math/Rectangle.h @@ -0,0 +1,127 @@ +#ifndef Rectangle_h__ +#define Rectangle_h__ + +#include "math/Vector2D.h" + +namespace LifeMath +{ + template + struct Rectangle + { + T x; + T y; + T width; + T height; + Rectangle() + { + x = y = width = height = 0; + } + Rectangle(T _width, T _height) + { + width = _width; + height = _height; + x = y = 0; + } + Rectangle(T _x, T _y, T _width, T _height) + { + width = _width; + height = _height; + x = _x; + y = _y; + } + bool operator ==(const Rectangle& other) + { + return ((x == other.x) && (y == other.y) && (width == other.width) && (height == other.height)); + } + + bool operator !=(const Rectangle& other) + { + return ((x != other.x) || (y != other.y) || (width != other.width) || (height != other.height)); + } + + //get rectangle minimum corner coordinate + Vector2D GetMin() const + { + return Vector2D(x, y); + } + //get rectangle maximum corner coordinate + Vector2D GetMax() const + { + return Vector2D(x + (width), y + (height)); + } + //get rectangle maximum X + T GetMaxX() const + { + return x + (width); + } + //get rectangle maximum Y + T GetMaxY() const + { + return y + (height); + } + //get rectangle square + T GetSquare() const + { + return width * height; + } + //merge 2 rectangles into 1 bounding rectangle + void Merge(const Rectangle& other) + { + int minX = std::min(x, other.x); + int maxX = std::max(this->GetMaxX(), other.GetMaxX()); + + int minY = std::min(y, other.y); + int maxY = std::max(this->GetMaxY(), other.GetMaxY()); + + this->x = minX; + this->y = minY; + + this->width = maxX - minX; + this->height = maxY - minY; + } + //crop this rectangle by other rectangle + void Crop(const Rectangle& other) + { + if(this->x < other.x) + { + T diff = other.x - this->x; + this->x += diff; + this->width -= diff; + } + if(this->y < other.y) + { + T diff = other.y - this->y; + this->y += diff; + this->height -= diff; + } + if(this->GetMaxX() > other.GetMaxX()) + { + T diff = this->GetMaxX() - other.GetMaxX(); + this->width -= diff; + } + if(this->GetMaxY() > other.GetMaxY()) + { + T diff = this->GetMaxY() - other.GetMaxY(); + this->height -= diff; + } + + if(width < 0) + width = 0; + if(height < 0) + height = 0; + } + bool InRect(const T x, const T y) + { + return (x >= this->x && y >= this->y && x <= (this->x + width) && y <=(this->y + height)); + } + bool InRect(const Vector2D& point) + { + return InRect(point.X, point.Y); + } + }; + + typedef Rectangle Rect2i; + typedef Rectangle Rect2f; + typedef Rectangle Rect2d; +} +#endif // Rectangle_h__ \ No newline at end of file diff --git a/LifeCore/math/Spline.h b/LifeCore/math/Spline.h new file mode 100644 index 0000000..5675aad --- /dev/null +++ b/LifeCore/math/Spline.h @@ -0,0 +1,22 @@ +#ifndef Spline_h__ +#define Spline_h__ + +namespace LifeCore +{ + template + static inline T LinearBezier(float t, const T& v1, const T& v2) + { + return (v1 * (1-t)) + (v2 * t); + } + template + static inline T QuadraticBezier(float t, const T& v1, const T& v2, const T& v3) + { + return (LinearBezier(t, v1, v2) * (1 - t)) + (LinearBezier(t, v2, v3) * (t)); + } + template + static inline T CubicBezier(float t, const T& v1, const T& v2, const T& v3, const T& v4) + { + return (QuadraticBezier(t, v1, v2, v3) * (1 - t)) + (QuadraticBezier(t, v2, v3, v4) * (t)); + } +} +#endif // Spline_h__ \ No newline at end of file diff --git a/LifeCore/math/Vector2D.h b/LifeCore/math/Vector2D.h new file mode 100644 index 0000000..856eb8a --- /dev/null +++ b/LifeCore/math/Vector2D.h @@ -0,0 +1,175 @@ +#ifndef __Vector2D__ +#define __Vector2D__ +#include "Math.h" + +namespace LifeMath +{ +#undef INLINE +#ifdef _WIN32 +#ifdef _MSC_VER +#define INLINE __forceinline +#endif +#else +#ifdef __GNUC__ +#define INLINE __attribute__((always_inline)) +#endif +#endif + +#ifndef INLINE +#define INLINE inline +#endif + + template + struct Vector2D + { + public: + T X, Y; + INLINE Vector2D(T x, T y) + { + this->X = x; + this->Y = y; + } + INLINE void Set(T x, T y) + { + this->X = x; + this->Y = y; + } + + INLINE Vector2D(void) + { + this->X = this->Y = 0; + } + + INLINE Vector2D(T val) + { + this->X = val; + this->Y = val; + } + INLINE Vector2D& operator=(const Vector2D& other) + { + X = other.X; + Y = other.Y; + return *this; + } + //unary operators + INLINE Vector2D operator -() const + { + return Vector2D(-X, -Y); + } + + INLINE Vector2D operator +() const + { + return *this; + } + + //binary operators + INLINE Vector2D operator +(const LifeMath::Vector2D &other) const + { + return Vector2D(X + other.X, Y + other.Y); + } + INLINE Vector2D operator -(const LifeMath::Vector2D &other) const + { + return Vector2D(X - other.X, Y - other.Y); + } + INLINE Vector2D operator *(T other) const + { + return Vector2D(X * other, Y * other); + } + INLINE Vector2D operator /(T other) const + { + return Vector2D(X / other, Y / other); + } + + INLINE operator T * () + { + return &X; + } + INLINE operator const T * () const + { + return &X; + } + INLINE bool operator == (const Vector2D& vector) const + { + return (X == vector.X && Y == vector.Y); + } + + INLINE bool operator != (const Vector2D& vector) const + { + return (X != vector.X || Y != vector.Y); + } + INLINE Vector2D& operator += (const Vector2D& Vector) + { + X += Vector.X; + Y += Vector.Y; + return *this; + } + INLINE Vector2D& operator -= (const Vector2D& Vector) + { + X -= Vector.X; + Y -= Vector.Y; + return *this; + } + INLINE Vector2D& operator *= (T Value) + { + X *= Value; + Y *= Value; + return *this; + } + INLINE Vector2D& operator /= (T Value) + { + //assert(Value && "Zero Value"); + //*this *= (1.0f / Value); + this->X /= Value; + this->Y /= Value; + + return *this; + } + INLINE Vector2D& operator += (T Value) + { + X += Value; + Y += Value; + return *this; + } + INLINE Vector2D& operator -= (T Value) + { + X -= Value; + Y -= Value; + return *this; + } + INLINE Vector2D& operator /= (const Vector2D& vector) + { + X /= vector.X; + Y /= vector.Y; + return *this; + } + INLINE Vector2D& operator *= (const Vector2D& vector) + { + X *= vector.X; + Y *= vector.Y; + return *this; + } + + INLINE float& operator [](int index) + { + //assert(index < 3 && "Out Of Range"); + return *(index + (&X)); + } + INLINE float operator [] (int index) const + { + //assert(index < 2 && "Out Of Range"); + return * (index + (&X)); + } + INLINE bool operator > (const Vector2D& v) const + { + return X > v.X && Y > v.Y; + } + INLINE bool operator < (const Vector2D& v) const + { + return X < v.X && Y < v.Y; + } + }; + typedef Vector2D Vec2f; + typedef Vector2D Vec2i; + typedef Vector2D float2; +} +#endif \ No newline at end of file diff --git a/LifeCore/math/Vector3D.h b/LifeCore/math/Vector3D.h new file mode 100644 index 0000000..a9c0d8a --- /dev/null +++ b/LifeCore/math/Vector3D.h @@ -0,0 +1,180 @@ +#ifndef __VECTOR3D__ +#define __VECTOR3D__ +#include "Math.h" + +namespace LifeMath +{ +#undef INLINE +#ifdef _WIN32 +#ifdef _MSC_VER +#define INLINE __forceinline +#endif +#else +#ifdef __GNUC__ +#define INLINE __attribute__((always_inline)) +#endif +#endif + +#ifndef INLINE +#define INLINE inline +#endif + + template + struct Vector3D + { + public: + T X, Y, Z; + INLINE Vector3D(T x, T y, T z) + { + this->X = x; + this->Y = y; + this->Z = z; + } + INLINE Vector3D(void) + { + this->X = 0.0f; + this->Y = 0.0f; + this->Z = 0.0f; + } + + INLINE Vector3D(T val) + { + this->X = val; + this->Y = val; + this->Z = val; + } + + INLINE Vector3D& operator=(const Vector3D& other) + { + X = other.X; + Y = other.Y; + Z = other.Z; + return *this; + } + //unary operators + INLINE Vector3D operator -() const + { + return Vector3D(-X, -Y, -Z); + } + + INLINE Vector3D operator +() const + { + return *this; + } + + //binary operators + INLINE Vector3D operator +(const Vector3D &other) const + { + return Vector3D(X + other.X, Y + other.Y, Z + other.Z); + } + INLINE Vector3D operator -(const Vector3D &other) const + { + return Vector3D(X - other.X, Y - other.Y, Z - other.Z); + } + INLINE Vector3D operator *(T other) const + { + return Vector3D(X * other, Y * other, Z * other); + } + INLINE Vector3D operator /(T other) const + { + return Vector3D(X / other, Y / other, Z / other); + } + + INLINE operator T * () + { + return &X; + } + INLINE operator const T * () const + { + return &X; + } + INLINE bool operator == (const Vector3D& vector) const + { + return (X == vector.X && Y == vector.Y && Z == vector.Z); + } + + INLINE bool operator != (const Vector3D& vector) const + { + return (X != vector.X || Y != vector.Y || Z != vector.Z); + } + INLINE Vector3D& operator += (const Vector3D& Vector) + { + X += Vector.X; + Y += Vector.Y; + Z += Vector.Z; + return *this; + } + INLINE Vector3D& operator -= (const Vector3D& Vector) + { + X -= Vector.X; + Y -= Vector.Y; + Z -= Vector.Z; + return *this; + } + INLINE Vector3D& operator *= (T Value) + { + X *= Value; + Y *= Value; + Z *= Value; + return *this; + } + INLINE Vector3D& operator /= (T Value) + { + //assert(Value && "Zero Value"); + *this *= (1.0f / Value); + return *this; + } + INLINE Vector3D& operator += (T Value) + { + X += Value; + Y += Value; + Z += Value; + return *this; + } + INLINE Vector3D& operator -= (T Value) + { + X -= Value; + Y -= Value; + Z -= Value; + return *this; + } + INLINE Vector3D& operator /= (const Vector3D& vector) + { + X /= vector.X; + Y /= vector.Y; + Z /= vector.Z; + return *this; + } + INLINE Vector3D& operator *= (const Vector3D& vector) + { + X *= vector.X; + Y *= vector.Y; + Z *= vector.Z; + return *this; + } + + INLINE T& operator [](int index) + { + //assert(index < 3 && "Out Of Range"); + return *(index + (&X)); + } + INLINE T operator [] (int index) const + { + //assert(index < 2 && "Out Of Range"); + return * (index + (&X)); + } + INLINE bool operator > (const Vector3D& v) const + { + return X > v.X && Y > v.Y && Z > v.Z; + } + INLINE bool operator < (const Vector3D& v) const + { + return X < v.X && Y < v.Y && Z < v.Z; + } + }; + + typedef Vector3D Vec3f; + typedef Vector3D Vec3i; + typedef Vector3D float3; +} +#endif \ No newline at end of file diff --git a/LifeCore/math/Vector4D.h b/LifeCore/math/Vector4D.h new file mode 100644 index 0000000..4cfb7f1 --- /dev/null +++ b/LifeCore/math/Vector4D.h @@ -0,0 +1,192 @@ +#ifndef __VECTOR4D__ +#define __VECTOR4D__ +#include "Math.h" + +namespace LifeMath +{ +#undef INLINE +#ifdef _WIN32 +#ifdef _MSC_VER +#define INLINE __forceinline +#endif +#else +#ifdef __GNUC__ +#define INLINE __attribute__((always_inline)) +#endif +#endif + +#ifndef INLINE +#define INLINE inline +#endif + + // struct Vector4D; + // struct Vector4D; + + template + struct Vector4D + { + public: + T X, Y, Z, W; + INLINE Vector4D(T x, T y, T z, T w) + { + this->X = x; + this->Y = y; + this->Z = z; + this->W = w; + } + INLINE Vector4D(void) + { + this->X = 0; + this->Y = 0; + this->Z = 0; + this->W = 0; + } + + INLINE Vector4D(T val) + { + this->X = val; + this->Y = val; + this->Z = val; + this->W = val; + } + //unary operators + INLINE Vector4D operator -() const + { + return Vector4D(-X, -Y, -Z, -W); + } + + INLINE Vector4D operator +() const + { + return *this; + } + INLINE Vector4D& operator=(const Vector4D& other) + { + X = other.X; + Y = other.Y; + Z = other.Z; + W = other.W; + return *this; + } + //binary operators + INLINE Vector4D operator +(const Vector4D &other) const + { + return Vector4D(X + other.X, Y + other.Y, Z + other.Z, W + other.W); + } + INLINE Vector4D operator -(const Vector4D &other) const + { + return Vector4D(X - other.X, Y - other.Y, Z - other.Z, W - other.W); + } + INLINE Vector4D operator *(T other) const + { + return Vector4D(X * other, Y * other, Z * other, W * other); + } + INLINE Vector4D operator /(T other) const + { + return Vector4D(X / other, Y / other, Z / other, W / other); + } + + INLINE operator T * () + { + return &X; + } + INLINE operator const T * () const + { + return &X; + } + INLINE bool operator == (const Vector4D& vector) const + { + return (X == vector.X && Y == vector.Y && Z == vector.Z && W == vector.W); + } + + INLINE bool operator != (const Vector4D& vector) const + { + return (X != vector.X || Y != vector.Y || Z != vector.Z || W != vector.W); + } + INLINE Vector4D& operator += (const Vector4D& Vector) + { + X += Vector.X; + Y += Vector.Y; + Z += Vector.Z; + W += Vector.W; + return *this; + } + INLINE Vector4D& operator -= (const Vector4D& Vector) + { + X -= Vector.X; + Y -= Vector.Y; + Z -= Vector.Z; + W -= Vector.W; + return *this; + } + INLINE Vector4D& operator *= (T Value) + { + X *= Value; + Y *= Value; + Z *= Value; + W *= Value; + return *this; + } + INLINE Vector4D& operator /= (T Value) + { + //assert(Value && "Zero Value"); + *this *= (1.0f / Value); + return *this; + } + INLINE Vector4D& operator += (T Value) + { + X += Value; + Y += Value; + Z += Value; + W += Value; + return *this; + } + INLINE Vector4D& operator -= (T Value) + { + X -= Value; + Y -= Value; + Z -= Value; + W -= Value; + return *this; + } + INLINE Vector4D& operator /= (const Vector4D& vector) + { + X /= vector.X; + Y /= vector.Y; + Z /= vector.Z; + W /= vector.W; + return *this; + } + INLINE Vector4D& operator *= (const Vector4D& vector) + { + X *= vector.X; + Y *= vector.Y; + Z *= vector.Z; + W *= vector.W; + return *this; + } + + INLINE T& operator [](int index) + { + //assert(index < 3 && "Out Of Range"); + return *(index + (&X)); + } + INLINE T operator [] (int index) const + { + //assert(index < 2 && "Out Of Range"); + return * (index + (&X)); + } + INLINE bool operator > (const Vector4D& v) const + { + return X > v.X && Y > v.Y && Z > v.Z && W > v.W; + } + INLINE bool operator < (const Vector4D& v) const + { + return X < v.X && Y < v.Y && Z < v.Z && W < v.W; + } + }; + + typedef Vector4D Vec4f; + typedef Vector4D Vec4i; + typedef Vector4D float4; +} +#endif \ No newline at end of file diff --git a/LifeCore/math/vector_base.h b/LifeCore/math/vector_base.h new file mode 100644 index 0000000..d115a9d --- /dev/null +++ b/LifeCore/math/vector_base.h @@ -0,0 +1,17 @@ +#ifndef vector_base_h__ +#define vector_base_h__ + +namespace LifeMath +{ + template + class vector_base + { + public: + vector_base(); + ~vector_base(); + + private: + + }; +} +#endif // vector_base_h__ diff --git a/LifeCore/media/WebCamera.cpp b/LifeCore/media/WebCamera.cpp new file mode 100644 index 0000000..5a4bf16 --- /dev/null +++ b/LifeCore/media/WebCamera.cpp @@ -0,0 +1,225 @@ +#include "WebCamera.h" + + +#include "../Utils.h" +#include "../log/NLog.h" +#include "../Com.h" + +namespace LifeCore { + class WinWebCameraDescription : public WebCameraDescription { + public: + friend class WebCamera; + WinWebCameraDescription(IMFActivate* act, int id) + : activate(act), deviceId(id) + { + if(activate != nullptr) + activate->AddRef(); + } + ~WinWebCameraDescription() { + SafeRelease(&activate); + } + std::wstring GetName() override{ + if(activate == nullptr) + return std::wstring(L""); + WCHAR *szFriendlyName = NULL; + UINT32 cchName; + auto hr = activate->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &szFriendlyName, &cchName); + if(hr != S_OK) + return std::wstring(L""); + std::wstring result(szFriendlyName); + CoTaskMemFree(szFriendlyName); + return result; + } + int deviceId; + protected: + IMFActivate* activate; + }; + struct ChooseDeviceParam { + ChooseDeviceParam() : ppDevices(NULL), count(0) {} + ~ChooseDeviceParam() { + for(DWORD i = 0; i < count; i++) { + SafeRelease(&ppDevices[i]); + } + CoTaskMemFree(ppDevices); + } + + IMFActivate **ppDevices; + UINT32 count; + UINT32 selection; + }; + class WinWebCamCallback : IMFCaptureEngineOnEventCallback { + public: + WinWebCamCallback(WebCamera* cam) + : m_cRef(1), camera(cam), sleeping(false) + { + + } + // IUnknown + STDMETHODIMP QueryInterface(REFIID riid, void** ppv) { + static const QITAB qit[] = { + QITABENT(WinWebCamCallback, IMFCaptureEngineOnEventCallback), { 0 } + }; + return QISearch(this, qit, riid, ppv); + } + STDMETHODIMP_(ULONG) AddRef() { + return InterlockedIncrement(&m_cRef); + } + STDMETHODIMP_(ULONG) Release() { + LONG cRef = InterlockedDecrement(&m_cRef); + if(cRef == 0) { + delete this; + } + return cRef; + } + + // IMFCaptureEngineOnEventCallback + STDMETHODIMP OnEvent(_In_ IMFMediaEvent* pEvent) { + if(sleeping && camera != nullptr) { + // We're about to fall asleep, that means we've just asked the CE to stop the preview + // and record. We need to handle it here since our message pump may be gone. + GUID guidType; + HRESULT hrStatus; + HRESULT hr = pEvent->GetStatus(&hrStatus); + if(FAILED(hr)) { + hrStatus = hr; + } + + hr = pEvent->GetExtendedType(&guidType); + if(SUCCEEDED(hr)) { + if(guidType == MF_CAPTURE_ENGINE_PREVIEW_STOPPED) { + LogInfo() << "Preview stopped"; + //m_pManager->OnPreviewStopped(hrStatus); +// SetEvent(m_pManager->m_hEvent); + } else if(guidType == MF_CAPTURE_ENGINE_RECORD_STOPPED) { + LogInfo() << "Engine stopped"; + //m_pManager->OnRecordStopped(hrStatus); + //SetEvent(m_pManager->m_hEvent); + } else { + // This is an event we don't know about, we don't really care and there's + // no clean way to report the error so just set the event and fall through. + //SetEvent(m_pManager->m_hEvent); + } + } + + return S_OK; + } else { + LogInfo() << "Capture event"; + pEvent->AddRef(); // The application will release the pointer when it handles the message. + camera->OnCaptureEvent(pEvent); + //PostMessage(m_hwnd, WM_APP_CAPTURE_EVENT, (WPARAM)pEvent, 0L); + } + + return S_OK; + } + protected: + bool sleeping; + long m_cRef; + WebCamera* camera; + }; + + WebCamera::WebCamera(webcam_desc_ptr desc) + : description(desc), FrameNumber(0), pSource(nullptr), grabber(nullptr) + { + callback = new WinWebCamCallback(this); + } + void WebCamera::OnCaptureEvent(IMFMediaEvent* pEvent) { + HRESULT hrStatus; + GUID eventType; + pEvent->GetStatus(&hrStatus); + pEvent->GetExtendedType(&eventType); + + } + + RawBuffer* WebCamera::GetImage() { + return grabber->GetImage(); + } + + void WebCamera::OnImageGrabbed(WinMfImageGrabber* grabber, RawBuffer* image) { + FrameNumber++; + Event_ImageCapture.Call(this, image); + } + bool WebCamera::Open() { + IMFActivate* pActivate = dynamic_cast(description.get())->activate; + + HRESULT hr = pActivate->ActivateObject(__uuidof(IMFMediaSource), (void**)&pSource); + if(FAILED(hr)) { + LogInfo() << "Failed to activate camera!"; + return false; + } + + grabber = new WinMfImageGrabber(this); + if(FAILED(grabber->initImageGrabber(pSource, MFVideoFormat_RGB24)) || FAILED(grabber->startGrabbing())) { + LogInfo() << "Failed to init camera grabber"; + pActivate->ShutdownObject(); + FINAL_RELEASE(grabber); + FINAL_RELEASE(pSource); + return false; + } + return true; + } + void WebCamera::Close() { + IMFActivate* pActivate = dynamic_cast(description.get())->activate; + if(grabber != nullptr) + grabber->stopGrabbing(); + FINAL_RELEASE(grabber); + FINAL_RELEASE(pSource); + + pActivate->ShutdownObject(); + + pActivate->AddRef(); + auto val = pActivate->Release(); + LogInfo() << "Initial 2 pActivate refCount = " << val; + FrameNumber = 0; + } + int2 WebCamera::GetSize() { + return int2(); + } + int32 WebCamera::GetBpp() { + return -1; + } + bool WebCamera::Read(int2 size, uint8* buffer) { + return false; + } + + void WebCamera::Update() { + grabber->processGrabbing(); + } + std::vector WebCamera::GetConnectedDevices() { + std::vector result; + + IMFAttributes *pAttributes = NULL; + ChooseDeviceParam param; + + HRESULT hr = MFCreateAttributes(&pAttributes, 1); + if(FAILED(hr)) { + printf("Failed\n"); + goto Exit; + } + + // Ask for source type = video capture devices + hr = pAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID); + if(FAILED(hr)) { + printf("Failed\n"); + goto Exit; + } + + // Enumerate devices. + hr = MFEnumDeviceSources(pAttributes, ¶m.ppDevices, ¶m.count); + if(FAILED(hr)) { + printf("Failed\n"); + goto Exit; + } + + for(UINT32 i = 0; i < param.count; ++i) { + result.push_back(webcam_desc_ptr(new WinWebCameraDescription(param.ppDevices[i], i))); + } + + Exit: + if(NULL != pAttributes) { + pAttributes->Release(); + pAttributes = NULL; + } + return result; + } +} \ No newline at end of file diff --git a/LifeCore/media/WebCamera.h b/LifeCore/media/WebCamera.h new file mode 100644 index 0000000..9acea3c --- /dev/null +++ b/LifeCore/media/WebCamera.h @@ -0,0 +1,54 @@ +#ifndef WebCamera_h__ +#define WebCamera_h__ + +#include "../ICamera.h" +#include "../IImage.h" +#include +#include +#include +#include +#include "WinMfImageGrabber.h" +#include +#pragma comment(lib, "mf.lib") +#pragma comment(lib, "mfplat.lib") +#pragma comment(lib, "mfuuid.lib") +#pragma comment(lib, "shlwapi.lib") + + +#include +#include +#include "../Delegate.h" + +namespace LifeCore { + class WebCameraDescription { + public: + virtual ~WebCameraDescription() {} + virtual std::wstring GetName() = 0; + }; + + typedef std::shared_ptr webcam_desc_ptr; + + class WebCamera : public ICamera, public IImage + { + public: + Delegate*> Event_ImageCapture; + WebCamera(webcam_desc_ptr desc); + int2 GetSize(); + int32 GetBpp(); + bool Read(int2 size, uint8* buffer); + static std::vector GetConnectedDevices(); + bool Open(); + void Close(); + void OnImageGrabbed(WinMfImageGrabber* grabber, RawBuffer* image); + void OnCaptureEvent(IMFMediaEvent* pEvent); + void Update(); + RawBuffer* GetImage(); + int FrameNumber; + private: + webcam_desc_ptr description; + WinMfImageGrabber* grabber; + IMFMediaSource *pSource; + void* callback; + }; +} +#endif // WebCamera_h__ diff --git a/LifeCore/media/WinMfFormatReader.cpp b/LifeCore/media/WinMfFormatReader.cpp new file mode 100644 index 0000000..b5eaa36 --- /dev/null +++ b/LifeCore/media/WinMfFormatReader.cpp @@ -0,0 +1,461 @@ +#include "WinMfFormatReader.h" +#include "Strsafe.h" +#include +#include + +namespace LifeCore { + LPCWSTR GetGUIDNameConstNew(const GUID& guid); + HRESULT GetGUIDNameNew(const GUID& guid, WCHAR **ppwsz); + + HRESULT LogAttributeValueByIndexNew(IMFAttributes *pAttr, DWORD index); + HRESULT SpecialCaseAttributeValueNew(GUID guid, const PROPVARIANT& var, WinMfMediaType &out); + + unsigned int *GetParametr(GUID guid, WinMfMediaType &out) { + if(guid == MF_MT_YUV_MATRIX) + return &(out.MF_MT_YUV_MATRIX); + + if(guid == MF_MT_VIDEO_LIGHTING) + return &(out.MF_MT_VIDEO_LIGHTING); + + if(guid == MF_MT_DEFAULT_STRIDE) + return &(out.MF_MT_DEFAULT_STRIDE); + + if(guid == MF_MT_VIDEO_CHROMA_SITING) + return &(out.MF_MT_VIDEO_CHROMA_SITING); + + if(guid == MF_MT_VIDEO_NOMINAL_RANGE) + return &(out.MF_MT_VIDEO_NOMINAL_RANGE); + + if(guid == MF_MT_ALL_SAMPLES_INDEPENDENT) + return &(out.MF_MT_ALL_SAMPLES_INDEPENDENT); + + if(guid == MF_MT_FIXED_SIZE_SAMPLES) + return &(out.MF_MT_FIXED_SIZE_SAMPLES); + + if(guid == MF_MT_SAMPLE_SIZE) + return &(out.MF_MT_SAMPLE_SIZE); + + if(guid == MF_MT_VIDEO_PRIMARIES) + return &(out.MF_MT_VIDEO_PRIMARIES); + + if(guid == MF_MT_INTERLACE_MODE) + return &(out.MF_MT_INTERLACE_MODE); + + return NULL; + } + + HRESULT LogAttributeValueByIndexNew(IMFAttributes *pAttr, DWORD index, WinMfMediaType &out) { + WCHAR *pGuidName = NULL; + WCHAR *pGuidValName = NULL; + + GUID guid = { 0 }; + + PROPVARIANT var; + PropVariantInit(&var); + + HRESULT hr = pAttr->GetItemByIndex(index, &guid, &var); + + if(FAILED(hr)) { + goto done; + } + + hr = GetGUIDNameNew(guid, &pGuidName); + + if(FAILED(hr)) { + goto done; + } + + hr = SpecialCaseAttributeValueNew(guid, var, out); + + unsigned int *p; + + if(FAILED(hr)) { + goto done; + } + if(hr == S_FALSE) { + switch(var.vt) { + case VT_UI4: + + p = GetParametr(guid, out); + + if(p) { + *p = var.ulVal; + } + + break; + + case VT_UI8: + + break; + + case VT_R8: + + break; + + case VT_CLSID: + if(guid == MF_MT_AM_FORMAT_TYPE) { + hr = GetGUIDNameNew(*var.puuid, &pGuidValName); + + if(SUCCEEDED(hr)) { + out.MF_MT_AM_FORMAT_TYPE = MF_MT_AM_FORMAT_TYPE; + + out.pMF_MT_AM_FORMAT_TYPEName = pGuidValName; + + pGuidValName = NULL; + } + } + + if(guid == MF_MT_MAJOR_TYPE) { + hr = GetGUIDNameNew(*var.puuid, &pGuidValName); + + if(SUCCEEDED(hr)) { + out.MF_MT_MAJOR_TYPE = MF_MT_MAJOR_TYPE; + + out.pMF_MT_MAJOR_TYPEName = pGuidValName; + + pGuidValName = NULL; + } + } + + if(guid == MF_MT_SUBTYPE) { + hr = GetGUIDNameNew(*var.puuid, &pGuidValName); + + if(SUCCEEDED(hr)) { + out.MF_MT_SUBTYPE = MF_MT_SUBTYPE; + + out.pMF_MT_SUBTYPEName = pGuidValName; + + pGuidValName = NULL; + } + } + + break; + + case VT_LPWSTR: + + break; + + case VT_VECTOR | VT_UI1: + + break; + + case VT_UNKNOWN: + + break; + + default: + + break; + } + } + + done: + CoTaskMemFree(pGuidName); + CoTaskMemFree(pGuidValName); + PropVariantClear(&var); + return hr; + } + + HRESULT GetGUIDNameNew(const GUID& guid, WCHAR **ppwsz) { + HRESULT hr = S_OK; + WCHAR *pName = NULL; + + LPCWSTR pcwsz = GetGUIDNameConstNew(guid); + if(pcwsz) { + size_t cchLength = 0; + + hr = StringCchLengthW(pcwsz, STRSAFE_MAX_CCH, &cchLength); + if(FAILED(hr)) { + goto done; + } + + pName = (WCHAR*)CoTaskMemAlloc((cchLength + 1) * sizeof(WCHAR)); + + if(pName == NULL) { + hr = E_OUTOFMEMORY; + goto done; + } + + hr = StringCchCopyW(pName, cchLength + 1, pcwsz); + if(FAILED(hr)) { + goto done; + } + } else { + hr = StringFromCLSID(guid, &pName); + } + + done: + if(FAILED(hr)) { + *ppwsz = NULL; + CoTaskMemFree(pName); + } else { + *ppwsz = pName; + } + return hr; + } + + void LogUINT32AsUINT64New(const PROPVARIANT& var, UINT32 &uHigh, UINT32 &uLow) { + Unpack2UINT32AsUINT64(var.uhVal.QuadPart, &uHigh, &uLow); + + } + + float OffsetToFloatNew(const MFOffset& offset) { + return offset.value + (static_cast(offset.fract) / 65536.0f); + } + + HRESULT LogVideoAreaNew(const PROPVARIANT& var) { + if(var.caub.cElems < sizeof(MFVideoArea)) { + return S_OK; + } + + MFVideoArea *pArea = (MFVideoArea*)var.caub.pElems; + + return S_OK; + } + + HRESULT SpecialCaseAttributeValueNew(GUID guid, const PROPVARIANT& var, WinMfMediaType &out) { + if(guid == MF_MT_FRAME_SIZE) { + UINT32 uHigh = 0, uLow = 0; + + LogUINT32AsUINT64New(var, uHigh, uLow); + + out.width = uHigh; + + out.height = uLow; + + out.MF_MT_FRAME_SIZE = out.width * out.height; + } else + if(guid == MF_MT_FRAME_RATE) { + UINT32 uHigh = 0, uLow = 0; + + LogUINT32AsUINT64New(var, uHigh, uLow); + + out.MF_MT_FRAME_RATE = uHigh; + + out.MF_MT_FRAME_RATE_low = uLow; + } else + if(guid == MF_MT_FRAME_RATE_RANGE_MAX) { + UINT32 uHigh = 0, uLow = 0; + + LogUINT32AsUINT64New(var, uHigh, uLow); + + out.MF_MT_FRAME_RATE_RANGE_MAX = uHigh; + + out.MF_MT_FRAME_RATE_RANGE_MAX_low = uLow; + } else + if(guid == MF_MT_FRAME_RATE_RANGE_MIN) { + UINT32 uHigh = 0, uLow = 0; + + LogUINT32AsUINT64New(var, uHigh, uLow); + + out.MF_MT_FRAME_RATE_RANGE_MIN = uHigh; + + out.MF_MT_FRAME_RATE_RANGE_MIN_low = uLow; + } else + if(guid == MF_MT_PIXEL_ASPECT_RATIO) { + UINT32 uHigh = 0, uLow = 0; + + LogUINT32AsUINT64New(var, uHigh, uLow); + + out.MF_MT_PIXEL_ASPECT_RATIO = uHigh; + + out.MF_MT_PIXEL_ASPECT_RATIO_low = uLow; + } else { + return S_FALSE; + } + return S_OK; + } + +#ifndef IF_EQUAL_RETURN +#define IF_EQUAL_RETURN(param, val) if(val == param) return L#val +#endif + + LPCWSTR GetGUIDNameConstNew(const GUID& guid) { + IF_EQUAL_RETURN(guid, MF_MT_MAJOR_TYPE); + IF_EQUAL_RETURN(guid, MF_MT_MAJOR_TYPE); + IF_EQUAL_RETURN(guid, MF_MT_SUBTYPE); + IF_EQUAL_RETURN(guid, MF_MT_ALL_SAMPLES_INDEPENDENT); + IF_EQUAL_RETURN(guid, MF_MT_FIXED_SIZE_SAMPLES); + IF_EQUAL_RETURN(guid, MF_MT_COMPRESSED); + IF_EQUAL_RETURN(guid, MF_MT_SAMPLE_SIZE); + IF_EQUAL_RETURN(guid, MF_MT_WRAPPED_TYPE); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_NUM_CHANNELS); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_SAMPLES_PER_SECOND); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_FLOAT_SAMPLES_PER_SECOND); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_AVG_BYTES_PER_SECOND); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_BLOCK_ALIGNMENT); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_BITS_PER_SAMPLE); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_VALID_BITS_PER_SAMPLE); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_SAMPLES_PER_BLOCK); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_CHANNEL_MASK); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_FOLDDOWN_MATRIX); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_WMADRC_PEAKREF); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_WMADRC_PEAKTARGET); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_WMADRC_AVGREF); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_WMADRC_AVGTARGET); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_PREFER_WAVEFORMATEX); + IF_EQUAL_RETURN(guid, MF_MT_AAC_PAYLOAD_TYPE); + IF_EQUAL_RETURN(guid, MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION); + IF_EQUAL_RETURN(guid, MF_MT_FRAME_SIZE); + IF_EQUAL_RETURN(guid, MF_MT_FRAME_RATE); + IF_EQUAL_RETURN(guid, MF_MT_FRAME_RATE_RANGE_MAX); + IF_EQUAL_RETURN(guid, MF_MT_FRAME_RATE_RANGE_MIN); + IF_EQUAL_RETURN(guid, MF_MT_PIXEL_ASPECT_RATIO); + IF_EQUAL_RETURN(guid, MF_MT_DRM_FLAGS); + IF_EQUAL_RETURN(guid, MF_MT_PAD_CONTROL_FLAGS); + IF_EQUAL_RETURN(guid, MF_MT_SOURCE_CONTENT_HINT); + IF_EQUAL_RETURN(guid, MF_MT_VIDEO_CHROMA_SITING); + IF_EQUAL_RETURN(guid, MF_MT_INTERLACE_MODE); + IF_EQUAL_RETURN(guid, MF_MT_TRANSFER_FUNCTION); + IF_EQUAL_RETURN(guid, MF_MT_VIDEO_PRIMARIES); + IF_EQUAL_RETURN(guid, MF_MT_CUSTOM_VIDEO_PRIMARIES); + IF_EQUAL_RETURN(guid, MF_MT_YUV_MATRIX); + IF_EQUAL_RETURN(guid, MF_MT_VIDEO_LIGHTING); + IF_EQUAL_RETURN(guid, MF_MT_VIDEO_NOMINAL_RANGE); + IF_EQUAL_RETURN(guid, MF_MT_GEOMETRIC_APERTURE); + IF_EQUAL_RETURN(guid, MF_MT_MINIMUM_DISPLAY_APERTURE); + IF_EQUAL_RETURN(guid, MF_MT_PAN_SCAN_APERTURE); + IF_EQUAL_RETURN(guid, MF_MT_PAN_SCAN_ENABLED); + IF_EQUAL_RETURN(guid, MF_MT_AVG_BITRATE); + IF_EQUAL_RETURN(guid, MF_MT_AVG_BIT_ERROR_RATE); + IF_EQUAL_RETURN(guid, MF_MT_MAX_KEYFRAME_SPACING); + IF_EQUAL_RETURN(guid, MF_MT_DEFAULT_STRIDE); + IF_EQUAL_RETURN(guid, MF_MT_PALETTE); + IF_EQUAL_RETURN(guid, MF_MT_USER_DATA); + IF_EQUAL_RETURN(guid, MF_MT_AM_FORMAT_TYPE); + IF_EQUAL_RETURN(guid, MF_MT_MPEG_START_TIME_CODE); + IF_EQUAL_RETURN(guid, MF_MT_MPEG2_PROFILE); + IF_EQUAL_RETURN(guid, MF_MT_MPEG2_LEVEL); + IF_EQUAL_RETURN(guid, MF_MT_MPEG2_FLAGS); + IF_EQUAL_RETURN(guid, MF_MT_MPEG_SEQUENCE_HEADER); + IF_EQUAL_RETURN(guid, MF_MT_DV_AAUX_SRC_PACK_0); + IF_EQUAL_RETURN(guid, MF_MT_DV_AAUX_CTRL_PACK_0); + IF_EQUAL_RETURN(guid, MF_MT_DV_AAUX_SRC_PACK_1); + IF_EQUAL_RETURN(guid, MF_MT_DV_AAUX_CTRL_PACK_1); + IF_EQUAL_RETURN(guid, MF_MT_DV_VAUX_SRC_PACK); + IF_EQUAL_RETURN(guid, MF_MT_DV_VAUX_CTRL_PACK); + IF_EQUAL_RETURN(guid, MF_MT_ARBITRARY_HEADER); + IF_EQUAL_RETURN(guid, MF_MT_ARBITRARY_FORMAT); + IF_EQUAL_RETURN(guid, MF_MT_IMAGE_LOSS_TOLERANT); + IF_EQUAL_RETURN(guid, MF_MT_MPEG4_SAMPLE_DESCRIPTION); + IF_EQUAL_RETURN(guid, MF_MT_MPEG4_CURRENT_SAMPLE_ENTRY); + IF_EQUAL_RETURN(guid, MF_MT_ORIGINAL_4CC); + IF_EQUAL_RETURN(guid, MF_MT_ORIGINAL_WAVE_FORMAT_TAG); + + // Media types + + IF_EQUAL_RETURN(guid, MFMediaType_Audio); + IF_EQUAL_RETURN(guid, MFMediaType_Video); + IF_EQUAL_RETURN(guid, MFMediaType_Protected); + IF_EQUAL_RETURN(guid, MFMediaType_SAMI); + IF_EQUAL_RETURN(guid, MFMediaType_Script); + IF_EQUAL_RETURN(guid, MFMediaType_Image); + IF_EQUAL_RETURN(guid, MFMediaType_HTML); + IF_EQUAL_RETURN(guid, MFMediaType_Binary); + IF_EQUAL_RETURN(guid, MFMediaType_FileTransfer); + + IF_EQUAL_RETURN(guid, MFVideoFormat_AI44); // FCC('AI44') + IF_EQUAL_RETURN(guid, MFVideoFormat_ARGB32); // D3DFMT_A8R8G8B8 + IF_EQUAL_RETURN(guid, MFVideoFormat_AYUV); // FCC('AYUV') + IF_EQUAL_RETURN(guid, MFVideoFormat_DV25); // FCC('dv25') + IF_EQUAL_RETURN(guid, MFVideoFormat_DV50); // FCC('dv50') + IF_EQUAL_RETURN(guid, MFVideoFormat_DVH1); // FCC('dvh1') + IF_EQUAL_RETURN(guid, MFVideoFormat_DVSD); // FCC('dvsd') + IF_EQUAL_RETURN(guid, MFVideoFormat_DVSL); // FCC('dvsl') + IF_EQUAL_RETURN(guid, MFVideoFormat_H264); // FCC('H264') + IF_EQUAL_RETURN(guid, MFVideoFormat_I420); // FCC('I420') + IF_EQUAL_RETURN(guid, MFVideoFormat_IYUV); // FCC('IYUV') + IF_EQUAL_RETURN(guid, MFVideoFormat_M4S2); // FCC('M4S2') + IF_EQUAL_RETURN(guid, MFVideoFormat_MJPG); + IF_EQUAL_RETURN(guid, MFVideoFormat_MP43); // FCC('MP43') + IF_EQUAL_RETURN(guid, MFVideoFormat_MP4S); // FCC('MP4S') + IF_EQUAL_RETURN(guid, MFVideoFormat_MP4V); // FCC('MP4V') + IF_EQUAL_RETURN(guid, MFVideoFormat_MPG1); // FCC('MPG1') + IF_EQUAL_RETURN(guid, MFVideoFormat_MSS1); // FCC('MSS1') + IF_EQUAL_RETURN(guid, MFVideoFormat_MSS2); // FCC('MSS2') + IF_EQUAL_RETURN(guid, MFVideoFormat_NV11); // FCC('NV11') + IF_EQUAL_RETURN(guid, MFVideoFormat_NV12); // FCC('NV12') + IF_EQUAL_RETURN(guid, MFVideoFormat_P010); // FCC('P010') + IF_EQUAL_RETURN(guid, MFVideoFormat_P016); // FCC('P016') + IF_EQUAL_RETURN(guid, MFVideoFormat_P210); // FCC('P210') + IF_EQUAL_RETURN(guid, MFVideoFormat_P216); // FCC('P216') + IF_EQUAL_RETURN(guid, MFVideoFormat_RGB24); // D3DFMT_R8G8B8 + IF_EQUAL_RETURN(guid, MFVideoFormat_RGB32); // D3DFMT_X8R8G8B8 + IF_EQUAL_RETURN(guid, MFVideoFormat_RGB555); // D3DFMT_X1R5G5B5 + IF_EQUAL_RETURN(guid, MFVideoFormat_RGB565); // D3DFMT_R5G6B5 + IF_EQUAL_RETURN(guid, MFVideoFormat_RGB8); + IF_EQUAL_RETURN(guid, MFVideoFormat_UYVY); // FCC('UYVY') + IF_EQUAL_RETURN(guid, MFVideoFormat_v210); // FCC('v210') + IF_EQUAL_RETURN(guid, MFVideoFormat_v410); // FCC('v410') + IF_EQUAL_RETURN(guid, MFVideoFormat_WMV1); // FCC('WMV1') + IF_EQUAL_RETURN(guid, MFVideoFormat_WMV2); // FCC('WMV2') + IF_EQUAL_RETURN(guid, MFVideoFormat_WMV3); // FCC('WMV3') + IF_EQUAL_RETURN(guid, MFVideoFormat_WVC1); // FCC('WVC1') + IF_EQUAL_RETURN(guid, MFVideoFormat_Y210); // FCC('Y210') + IF_EQUAL_RETURN(guid, MFVideoFormat_Y216); // FCC('Y216') + IF_EQUAL_RETURN(guid, MFVideoFormat_Y410); // FCC('Y410') + IF_EQUAL_RETURN(guid, MFVideoFormat_Y416); // FCC('Y416') + IF_EQUAL_RETURN(guid, MFVideoFormat_Y41P); + IF_EQUAL_RETURN(guid, MFVideoFormat_Y41T); + IF_EQUAL_RETURN(guid, MFVideoFormat_YUY2); // FCC('YUY2') + IF_EQUAL_RETURN(guid, MFVideoFormat_YV12); // FCC('YV12') + IF_EQUAL_RETURN(guid, MFVideoFormat_YVYU); + + IF_EQUAL_RETURN(guid, MFAudioFormat_PCM); // WAVE_FORMAT_PCM + IF_EQUAL_RETURN(guid, MFAudioFormat_Float); // WAVE_FORMAT_IEEE_FLOAT + IF_EQUAL_RETURN(guid, MFAudioFormat_DTS); // WAVE_FORMAT_DTS + IF_EQUAL_RETURN(guid, MFAudioFormat_Dolby_AC3_SPDIF); // WAVE_FORMAT_DOLBY_AC3_SPDIF + IF_EQUAL_RETURN(guid, MFAudioFormat_DRM); // WAVE_FORMAT_DRM + IF_EQUAL_RETURN(guid, MFAudioFormat_WMAudioV8); // WAVE_FORMAT_WMAUDIO2 + IF_EQUAL_RETURN(guid, MFAudioFormat_WMAudioV9); // WAVE_FORMAT_WMAUDIO3 + IF_EQUAL_RETURN(guid, MFAudioFormat_WMAudio_Lossless); // WAVE_FORMAT_WMAUDIO_LOSSLESS + IF_EQUAL_RETURN(guid, MFAudioFormat_WMASPDIF); // WAVE_FORMAT_WMASPDIF + IF_EQUAL_RETURN(guid, MFAudioFormat_MSP1); // WAVE_FORMAT_WMAVOICE9 + IF_EQUAL_RETURN(guid, MFAudioFormat_MP3); // WAVE_FORMAT_MPEGLAYER3 + IF_EQUAL_RETURN(guid, MFAudioFormat_MPEG); // WAVE_FORMAT_MPEG + IF_EQUAL_RETURN(guid, MFAudioFormat_AAC); // WAVE_FORMAT_MPEG_HEAAC + IF_EQUAL_RETURN(guid, MFAudioFormat_ADTS); // WAVE_FORMAT_MPEG_ADTS_AAC + + return NULL; + } + + WinMfFormatReader::WinMfFormatReader(void) {} + + WinMfMediaType WinMfFormatReader::Read(IMFMediaType *pType) { + UINT32 count = 0; + + HRESULT hr = S_OK; + + WinMfMediaType out; + + hr = pType->LockStore(); + + if(FAILED(hr)) { + return out; + } + + hr = pType->GetCount(&count); + + if(FAILED(hr)) { + return out; + } + + for(UINT32 i = 0; i < count; i++) { + hr = LogAttributeValueByIndexNew(pType, i, out); + + if(FAILED(hr)) { + break; + } + } + + hr = pType->UnlockStore(); + + if(FAILED(hr)) { + return out; + } + + return out; + } + + WinMfFormatReader::~WinMfFormatReader(void) {} +} \ No newline at end of file diff --git a/LifeCore/media/WinMfFormatReader.h b/LifeCore/media/WinMfFormatReader.h new file mode 100644 index 0000000..b09a2d3 --- /dev/null +++ b/LifeCore/media/WinMfFormatReader.h @@ -0,0 +1,21 @@ +#ifndef WinMfFormatReader_h__ +#define WinMfFormatReader_h__ + +#include +#include "WinMfMediaType.h" +struct IMFMediaType; + +namespace LifeCore { + + + + /// Class for parsing info from IMFMediaType into the local MediaType + class WinMfFormatReader { + public: + static WinMfMediaType Read(IMFMediaType *pType); + ~WinMfFormatReader(void); + private: + WinMfFormatReader(void); + }; +} +#endif // WinMfFormatReader_h__ diff --git a/LifeCore/media/WinMfImageGrabber.cpp b/LifeCore/media/WinMfImageGrabber.cpp new file mode 100644 index 0000000..8d9a0b6 --- /dev/null +++ b/LifeCore/media/WinMfImageGrabber.cpp @@ -0,0 +1,342 @@ +#include "WinMfImageGrabber.h" +#include +#include "../log/NLog.h" +#include "../Utils.h" +#include "WinMfFormatReader.h" +#include "WinMfMediaType.h" +#include +#include "WebCamera.h" +#include "../Com.h" + +namespace LifeCore { + WinMfImageGrabber::WinMfImageGrabber(WebCamera* cam) + : m_cRef(1), ig_pSession(nullptr), camera(cam) + { + + } + WinMfImageGrabber::~WinMfImageGrabber() { + LogInfo() << "~WinMfImageGrabber "; + /*if(ig_pSession) { + LogInfo() << "~WinMfImageGrabber shutdown session"; + ig_pSession->Shutdown(); + }*/ + } + HRESULT WinMfImageGrabber::initImageGrabber(IMFMediaSource *pSource, GUID VideoFormat) { + IMFActivate *pSinkActivate = NULL;/**/ + IMFMediaType *pType = NULL;/**/ + IMFPresentationDescriptor *pPD = NULL;/**/ + IMFStreamDescriptor *pSD = NULL;/**/ + IMFMediaTypeHandler *pHandler = NULL;/**/ + IMFMediaType *pCurrentType = NULL;/**/ + WinMfMediaType MT; + auto hr = pSource->CreatePresentationDescriptor(&pPD); + if(FAILED(hr)) { + LogError() << "Failed to create presentation descriptor!"; + goto err; + } + + BOOL fSelected; + hr = pPD->GetStreamDescriptorByIndex(0, &fSelected, &pSD); + if(FAILED(hr)) { + goto err; + } + + hr = pSD->GetMediaTypeHandler(&pHandler); + if(FAILED(hr)) { + goto err; + } + + DWORD cTypes = 0; + hr = pHandler->GetMediaTypeCount(&cTypes); + if(FAILED(hr)) { + goto err; + } + + if(cTypes > 0) { + hr = pHandler->GetCurrentMediaType(&pCurrentType); + + if(FAILED(hr)) { + goto err; + } + + MT = WinMfFormatReader::Read(pCurrentType); + } + + err: + SafeRelease(&pPD); + SafeRelease(&pSD); + SafeRelease(&pHandler); + SafeRelease(&pCurrentType); + + unsigned int sizeRawImage = 0; + if(VideoFormat == MFVideoFormat_RGB24) { + sizeRawImage = MT.MF_MT_FRAME_SIZE * 3; + } else if(VideoFormat == MFVideoFormat_RGB32) { + sizeRawImage = MT.MF_MT_FRAME_SIZE * 4; + } + + buffer_first = new RawBuffer(sizeRawImage, MT); + buffer_second = new RawBuffer(sizeRawImage, MT); + buffer_active = buffer_second; + + hr = MFCreateMediaType(&pType); + hr = pType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); + hr = pType->SetGUID(MF_MT_SUBTYPE, VideoFormat); + + // Create the sample grabber sink. + hr = MFCreateSampleGrabberSinkActivate(pType, this, &pSinkActivate); + + // To run as fast as possible, set this attribute (requires Windows 7): + hr = pSinkActivate->SetUINT32(MF_SAMPLEGRABBERSINK_IGNORE_CLOCK, TRUE); + + // Create the Media Session. + + hr = MFCreateMediaSession(NULL, &ig_pSession); + + // Create the topology. + hr = CreateTopology(pSource, pSinkActivate, &ig_pTopology); + + SafeRelease(&pSinkActivate); + SafeRelease(&pType); + return S_OK; + + } + + RawBuffer* WinMfImageGrabber::GetImage() { + return buffer_active; + } + +#define CHECK_HR(x) if (FAILED(x)) { goto done; } + HRESULT WinMfImageGrabber::CreateTopology(IMFMediaSource *pSource, IMFActivate *pSinkActivate, IMFTopology **ppTopo) { + IMFTopology *pTopology = NULL; + IMFPresentationDescriptor *pPD = NULL; + IMFStreamDescriptor *pSD = NULL; + IMFMediaTypeHandler *pHandler = NULL; + IMFTopologyNode *pNode1 = NULL; + IMFTopologyNode *pNode2 = NULL; + + HRESULT hr = S_OK; + DWORD cStreams = 0; + + CHECK_HR(hr = MFCreateTopology(&pTopology)); + CHECK_HR(hr = pSource->CreatePresentationDescriptor(&pPD)); + CHECK_HR(hr = pPD->GetStreamDescriptorCount(&cStreams)); + + for(DWORD i = 0; i < cStreams; i++) { + // In this example, we look for audio streams and connect them to the sink. + + BOOL fSelected = FALSE; + GUID majorType; + + CHECK_HR(hr = pPD->GetStreamDescriptorByIndex(i, &fSelected, &pSD)); + CHECK_HR(hr = pSD->GetMediaTypeHandler(&pHandler)); + CHECK_HR(hr = pHandler->GetMajorType(&majorType)); + + if(majorType == MFMediaType_Video && fSelected) { + CHECK_HR(hr = AddSourceNode(pTopology, pSource, pPD, pSD, &pNode1)); + CHECK_HR(hr = AddOutputNode(pTopology, pSinkActivate, 0, &pNode2)); + CHECK_HR(hr = pNode1->ConnectOutput(0, pNode2, 0)); + break; + } else { + CHECK_HR(hr = pPD->DeselectStream(i)); + } + SafeRelease(&pSD); + SafeRelease(&pHandler); + } + + *ppTopo = pTopology; + (*ppTopo)->AddRef(); + + done: + SAFE_RELEASE(pTopology); + SAFE_RELEASE(pNode1); + SAFE_RELEASE(pNode2); + SAFE_RELEASE(pPD); + SAFE_RELEASE(pSD); + SAFE_RELEASE(pHandler); + return hr; + } + + HRESULT WinMfImageGrabber::AddSourceNode( + IMFTopology *pTopology, // Topology. + IMFMediaSource *pSource, // Media source. + IMFPresentationDescriptor *pPD, // Presentation descriptor. + IMFStreamDescriptor *pSD, // Stream descriptor. + IMFTopologyNode **ppNode) // Receives the node pointer. + { + IMFTopologyNode *pNode = NULL; + + HRESULT hr = S_OK; + CHECK_HR(hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &pNode)); + CHECK_HR(hr = pNode->SetUnknown(MF_TOPONODE_SOURCE, pSource)); + CHECK_HR(hr = pNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, pPD)); + CHECK_HR(hr = pNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, pSD)); + CHECK_HR(hr = pTopology->AddNode(pNode)); + + // Return the pointer to the caller. + *ppNode = pNode; + (*ppNode)->AddRef(); + + done: + SafeRelease(&pNode); + return hr; + } + + HRESULT WinMfImageGrabber::AddOutputNode( + IMFTopology *pTopology, // Topology. + IMFActivate *pActivate, // Media sink activation object. + DWORD dwId, // Identifier of the stream sink. + IMFTopologyNode **ppNode) // Receives the node pointer. + { + IMFTopologyNode *pNode = NULL; + + HRESULT hr = S_OK; + CHECK_HR(hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &pNode)); + CHECK_HR(hr = pNode->SetObject(pActivate)); + CHECK_HR(hr = pNode->SetUINT32(MF_TOPONODE_STREAMID, dwId)); + CHECK_HR(hr = pNode->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE)); + CHECK_HR(hr = pTopology->AddNode(pNode)); + + // Return the pointer to the caller. + *ppNode = pNode; + (*ppNode)->AddRef(); + + done: + SafeRelease(&pNode); + return hr; + } + + + + HRESULT WinMfImageGrabber::startGrabbing(void) { + PROPVARIANT var; + PropVariantInit(&var); + + HRESULT hr = S_OK; + hr = ig_pSession->SetTopology(0, ig_pTopology); + hr = ig_pSession->Start(&GUID_NULL, &var); + return hr; + } + + bool WinMfImageGrabber::processGrabbing() { + HRESULT hrStatus = S_OK; + MediaEventType met; + HRESULT hr; + IMFMediaEvent *pEvent = NULL; + + if(!ig_pSession) return false; + hr = ig_pSession->GetEvent(0, &pEvent); + if(!SUCCEEDED(hr)) { + hr = S_OK; + + return false; + } + + hr = pEvent->GetStatus(&hrStatus); + if(!SUCCEEDED(hr)) { + hr = S_OK; + + return false; + } + + hr = pEvent->GetType(&met); + if(!SUCCEEDED(hr)) { + hr = S_OK; + + return false; + } + + if(met == MESessionEnded) { + LogInfo() << "IMAGEGRABBER VIDEODEVICE %i: MESessionEnded "; + + ig_pSession->Stop(); + + return false; + } + + if(met == MESessionStopped) { + LogInfo() << "IMAGEGRABBER VIDEODEVICE %i: MESessionStopped"; + + return false; + } + + + if(met == MEVideoCaptureDeviceRemoved) { + LogInfo() << "IMAGEGRABBER VIDEODEVICE %i: MEVideoCaptureDeviceRemoved"; + + return false; + } + + SafeRelease(&pEvent); + return true; + } + + void WinMfImageGrabber::stopGrabbing() { + if(ig_pSession) { + ig_pSession->Stop(); + ig_pSession->Shutdown(); + SAFE_RELEASE(ig_pSession); + SAFE_RELEASE(ig_pTopology); + } + + /*IMFMediaSource *ig_pSource; + IMFMediaSession *ig_pSession; + IMFTopology *ig_pTopology;*/ + + } + + // IUnknown methods + STDMETHODIMP WinMfImageGrabber::QueryInterface(REFIID iid, void** ppv) { + // Creation tab of shifting interfaces from start of this class + static const QITAB qit[] = { + + QITABENT(WinMfImageGrabber, IMFSampleGrabberSinkCallback), + QITABENT(WinMfImageGrabber, IMFClockStateSink), { 0 } + }; + return QISearch(this, qit, iid, ppv); + } + STDMETHODIMP_(ULONG) WinMfImageGrabber::AddRef() { + return InterlockedIncrement(&m_cRef); + } + STDMETHODIMP_(ULONG) WinMfImageGrabber::Release() { + ULONG cRef = InterlockedDecrement(&m_cRef); + if(cRef == 0) { + delete this; + } + return cRef; + } + + // IMFClockStateSink methods + STDMETHODIMP WinMfImageGrabber::OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset) { + return S_OK; + } + STDMETHODIMP WinMfImageGrabber::OnClockStop(MFTIME hnsSystemTime) { + return S_OK; + } + STDMETHODIMP WinMfImageGrabber::OnClockPause(MFTIME hnsSystemTime) { + return S_OK; + } + STDMETHODIMP WinMfImageGrabber::OnClockRestart(MFTIME hnsSystemTime) { + return S_OK; + } + STDMETHODIMP WinMfImageGrabber::OnClockSetRate(MFTIME hnsSystemTime, float flRate) { + return S_OK; + } + + // IMFSampleGrabberSinkCallback methods + STDMETHODIMP WinMfImageGrabber::OnSetPresentationClock(IMFPresentationClock* pClock) { + return S_OK; + } + STDMETHODIMP WinMfImageGrabber::OnProcessSample(REFGUID guidMajorMediaType, DWORD dwSampleFlags, + LONGLONG llSampleTime, LONGLONG llSampleDuration, const BYTE * pSampleBuffer, + DWORD dwSampleSize) + { + LogInfo() << "Process sample"; + buffer_active->SetData((void*)pSampleBuffer); + camera->OnImageGrabbed(this, buffer_active); + return 0; + } + STDMETHODIMP WinMfImageGrabber::OnShutdown() { + return S_OK; + } +} \ No newline at end of file diff --git a/LifeCore/media/WinMfImageGrabber.h b/LifeCore/media/WinMfImageGrabber.h new file mode 100644 index 0000000..98e87ea --- /dev/null +++ b/LifeCore/media/WinMfImageGrabber.h @@ -0,0 +1,65 @@ +#ifndef WinMfImageGrabber_h__ +#define WinMfImageGrabber_h__ + +#include +#include "../RawBuffer.h" +#include "../Types.h" + +namespace LifeCore { + + class WebCamera; + class WinMfImageGrabber : IMFSampleGrabberSinkCallback { + public: + WinMfImageGrabber(WebCamera* cam); + ~WinMfImageGrabber(); + HRESULT initImageGrabber(IMFMediaSource *pSource, GUID VideoFormat); + HRESULT startGrabbing(void); + void stopGrabbing(); + + // IUnknown methods + STDMETHODIMP QueryInterface(REFIID iid, void** ppv); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + // IMFClockStateSink methods + STDMETHODIMP OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset); + STDMETHODIMP OnClockStop(MFTIME hnsSystemTime); + STDMETHODIMP OnClockPause(MFTIME hnsSystemTime); + STDMETHODIMP OnClockRestart(MFTIME hnsSystemTime); + STDMETHODIMP OnClockSetRate(MFTIME hnsSystemTime, float flRate); + + // IMFSampleGrabberSinkCallback methods + STDMETHODIMP OnSetPresentationClock(IMFPresentationClock* pClock); + STDMETHODIMP OnProcessSample(REFGUID guidMajorMediaType, DWORD dwSampleFlags, + LONGLONG llSampleTime, LONGLONG llSampleDuration, const BYTE * pSampleBuffer, + DWORD dwSampleSize); + STDMETHODIMP OnShutdown(); + bool processGrabbing(); + RawBuffer* GetImage(); + private: + WebCamera* camera; + long m_cRef; + //IMFMediaSource *ig_pSource; + IMFMediaSession *ig_pSession; + IMFTopology *ig_pTopology; + RawBuffer* buffer_first; + RawBuffer* buffer_second; + RawBuffer* buffer_active; + + HRESULT CreateTopology(IMFMediaSource *pSource, IMFActivate *pSinkActivate, IMFTopology **ppTopo); + HRESULT AddSourceNode( + IMFTopology *pTopology, + IMFMediaSource *pSource, + IMFPresentationDescriptor *pPD, + IMFStreamDescriptor *pSD, + IMFTopologyNode **ppNode); + + HRESULT AddOutputNode( + IMFTopology *pTopology, + IMFActivate *pActivate, + DWORD dwId, + IMFTopologyNode **ppNode); + }; + +} +#endif // WinMfImageGrabber_h__ diff --git a/LifeCore/media/WinMfMediaType.h b/LifeCore/media/WinMfMediaType.h new file mode 100644 index 0000000..1d068f3 --- /dev/null +++ b/LifeCore/media/WinMfMediaType.h @@ -0,0 +1,129 @@ +#ifndef WinMfMediaType_h__ +#define WinMfMediaType_h__ + +#include + +namespace LifeCore { + // Structure for collecting info about types of video, which are supported by current video device + struct WinMfMediaType { + unsigned int MF_MT_FRAME_SIZE; + + unsigned int height; + + unsigned int width; + + unsigned int MF_MT_YUV_MATRIX; + + unsigned int MF_MT_VIDEO_LIGHTING; + + unsigned int MF_MT_DEFAULT_STRIDE; + + unsigned int MF_MT_VIDEO_CHROMA_SITING; + + GUID MF_MT_AM_FORMAT_TYPE; + + wchar_t *pMF_MT_AM_FORMAT_TYPEName; + + unsigned int MF_MT_FIXED_SIZE_SAMPLES; + + unsigned int MF_MT_VIDEO_NOMINAL_RANGE; + + unsigned int MF_MT_FRAME_RATE; + + unsigned int MF_MT_FRAME_RATE_low; + + unsigned int MF_MT_PIXEL_ASPECT_RATIO; + + unsigned int MF_MT_PIXEL_ASPECT_RATIO_low; + + unsigned int MF_MT_ALL_SAMPLES_INDEPENDENT; + + unsigned int MF_MT_FRAME_RATE_RANGE_MIN; + + unsigned int MF_MT_FRAME_RATE_RANGE_MIN_low; + + unsigned int MF_MT_SAMPLE_SIZE; + + unsigned int MF_MT_VIDEO_PRIMARIES; + + unsigned int MF_MT_INTERLACE_MODE; + + unsigned int MF_MT_FRAME_RATE_RANGE_MAX; + + unsigned int MF_MT_FRAME_RATE_RANGE_MAX_low; + + GUID MF_MT_MAJOR_TYPE; + + wchar_t *pMF_MT_MAJOR_TYPEName; + + GUID MF_MT_SUBTYPE; + + wchar_t *pMF_MT_SUBTYPEName; + + WinMfMediaType() { + pMF_MT_AM_FORMAT_TYPEName = NULL; + + pMF_MT_MAJOR_TYPEName = NULL; + + pMF_MT_SUBTYPEName = NULL; + + Clear(); + } + + ~WinMfMediaType() { + Clear(); + } + + void Clear() { + + MF_MT_FRAME_SIZE = 0; + + height = 0; + + width = 0; + + MF_MT_YUV_MATRIX = 0; + + MF_MT_VIDEO_LIGHTING = 0; + + MF_MT_DEFAULT_STRIDE = 0; + + MF_MT_VIDEO_CHROMA_SITING = 0; + + MF_MT_FIXED_SIZE_SAMPLES = 0; + + MF_MT_VIDEO_NOMINAL_RANGE = 0; + + MF_MT_FRAME_RATE = 0; + + MF_MT_FRAME_RATE_low = 0; + + MF_MT_PIXEL_ASPECT_RATIO = 0; + + MF_MT_PIXEL_ASPECT_RATIO_low = 0; + + MF_MT_ALL_SAMPLES_INDEPENDENT = 0; + + MF_MT_FRAME_RATE_RANGE_MIN = 0; + + MF_MT_FRAME_RATE_RANGE_MIN_low = 0; + + MF_MT_SAMPLE_SIZE = 0; + + MF_MT_VIDEO_PRIMARIES = 0; + + MF_MT_INTERLACE_MODE = 0; + + MF_MT_FRAME_RATE_RANGE_MAX = 0; + + MF_MT_FRAME_RATE_RANGE_MAX_low = 0; + + memset(&MF_MT_MAJOR_TYPE, 0, sizeof(GUID)); + + memset(&MF_MT_AM_FORMAT_TYPE, 0, sizeof(GUID)); + + memset(&MF_MT_SUBTYPE, 0, sizeof(GUID)); + } + }; +} +#endif // WinMfMediaType_h__ diff --git a/LifeCore/minizip/crypt.h b/LifeCore/minizip/crypt.h new file mode 100644 index 0000000..1b959e5 --- /dev/null +++ b/LifeCore/minizip/crypt.h @@ -0,0 +1,129 @@ +/* crypt.h -- base code for crypt/uncrypt ZIPfile + +Version 1.01e, February 12th, 2005 + +Copyright (C) 1998-2005 Gilles Vollant + +This code is a modified version of crypting code in Infozip distribution + +The encryption/decryption parts of this source code (as opposed to the +non-echoing password parts) were originally written in Europe. The +whole source package can be freely distributed, including from the USA. +(Prior to January 2000, re-export from the US was a violation of US law.) + +This encryption code is a direct transcription of the algorithm from +Roger Schlafly, described by Phil Katz in the file appnote.txt. This +file (appnote.txt) is distributed with the PKZIP program (even in the +version without encryption capabilities). + +If you don't need crypting in your application, just define symbols +NOCRYPT and NOUNCRYPT. + +This code support the "Traditional PKWARE Encryption". + +The new AES encryption added on Zip format by Winzip (see the page +http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong +Encryption is not supported. +*/ + +#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) + +/*********************************************************************** +* Return the next byte in the pseudo-random sequence +*/ +static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab) +{ + unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an + * unpredictable manner on 16-bit systems; not a problem + * with any known compiler so far, though */ + + temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; + return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); +} + +/*********************************************************************** +* Update the encryption keys with the next byte of plain text +*/ +static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c) +{ + (*(pkeys+0)) = CRC32((*(pkeys+0)), c); + (*(pkeys+1)) += (*(pkeys+0)) & 0xff; + (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; + { + register int keyshift = (int)((*(pkeys+1)) >> 24); + (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift); + } + return c; +} + +/*********************************************************************** +* Initialize the encryption keys and the random header according to +* the given password. +*/ +static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab) +{ + *(pkeys+0) = 305419896L; + *(pkeys+1) = 591751049L; + *(pkeys+2) = 878082192L; + while (*passwd != '\0') { + update_keys(pkeys,pcrc_32_tab,(int)*passwd); + passwd++; + } +} + +#define zdecode(pkeys,pcrc_32_tab,c) \ + (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) + +#define zencode(pkeys,pcrc_32_tab,c,t) \ + (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) + +#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED + +#define RAND_HEAD_LEN 12 +/* "last resort" source for second part of crypt seed pattern */ +# ifndef ZCR_SEED2 +# define ZCR_SEED2 3141592654UL /* use PI as default pattern */ +# endif + +static int crypthead(const char* passwd, /* password string */ + unsigned char* buf, /* where to write header */ + int bufSize, + unsigned long* pkeys, + const unsigned long* pcrc_32_tab, + unsigned long crcForCrypting) +{ + int n; /* index in random header */ + int t; /* temporary */ + int c; /* random byte */ + unsigned char header[RAND_HEAD_LEN-2]; /* random header */ + static unsigned calls = 0; /* ensure different random header each time */ + + if (bufSize> 7) & 0xff; + header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); + } + /* Encrypt random header (last two bytes is high word of crc) */ + init_keys(passwd, pkeys, pcrc_32_tab); + for (n = 0; n < RAND_HEAD_LEN-2; n++) + { + buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); + } + buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); + buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); + return n; +} + +#endif \ No newline at end of file diff --git a/LifeCore/minizip/ioapi.c b/LifeCore/minizip/ioapi.c new file mode 100644 index 0000000..37c1a27 --- /dev/null +++ b/LifeCore/minizip/ioapi.c @@ -0,0 +1,230 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip +part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + +Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + +Modifications for Zip64 support +Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + +For more info read MiniZip_info.txt + +*/ + +#if (defined(_WIN32)) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "ioapi.h" + +voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode) +{ + if (pfilefunc->zfile_func64.zopen64_file != NULL) + return (*(pfilefunc->zfile_func64.zopen64_file)) (pfilefunc->zfile_func64.opaque,filename,mode); + else + { + return (*(pfilefunc->zopen32_file))(pfilefunc->zfile_func64.opaque,(const char*)filename,mode); + } +} + +long call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin) +{ + if (pfilefunc->zfile_func64.zseek64_file != NULL) + return (*(pfilefunc->zfile_func64.zseek64_file)) (pfilefunc->zfile_func64.opaque,filestream,offset,origin); + else + { + uLong offsetTruncated = (uLong)offset; + if (offsetTruncated != offset) + return -1; + else + return (*(pfilefunc->zseek32_file))(pfilefunc->zfile_func64.opaque,filestream,offsetTruncated,origin); + } +} + +ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream) +{ + if (pfilefunc->zfile_func64.zseek64_file != NULL) + return (*(pfilefunc->zfile_func64.ztell64_file)) (pfilefunc->zfile_func64.opaque,filestream); + else + { + uLong tell_uLong = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream); + if ((tell_uLong) == ((uLong)-1)) + return (ZPOS64_T)-1; + else + return tell_uLong; + } +} + +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32) +{ + p_filefunc64_32->zfile_func64.zopen64_file = NULL; + p_filefunc64_32->zopen32_file = p_filefunc32->zopen_file; + p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; + p_filefunc64_32->zfile_func64.zread_file = p_filefunc32->zread_file; + p_filefunc64_32->zfile_func64.zwrite_file = p_filefunc32->zwrite_file; + p_filefunc64_32->zfile_func64.ztell64_file = NULL; + p_filefunc64_32->zfile_func64.zseek64_file = NULL; + p_filefunc64_32->zfile_func64.zclose_file = p_filefunc32->zclose_file; + p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; + p_filefunc64_32->zfile_func64.opaque = p_filefunc32->opaque; + p_filefunc64_32->zseek32_file = p_filefunc32->zseek_file; + p_filefunc64_32->ztell32_file = p_filefunc32->ztell_file; +} + +static voidpf ZCALLBACK fopen_file_func OF((voidpf opaque, const char* filename, int mode)); +static uLong ZCALLBACK fread_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +static uLong ZCALLBACK fwrite_file_func OF((voidpf opaque, voidpf stream, const void* buf,uLong size)); +static ZPOS64_T ZCALLBACK ftell64_file_func OF((voidpf opaque, voidpf stream)); +static long ZCALLBACK fseek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); +static int ZCALLBACK fclose_file_func OF((voidpf opaque, voidpf stream)); +static int ZCALLBACK ferror_file_func OF((voidpf opaque, voidpf stream)); + +static voidpf ZCALLBACK fopen_file_func (voidpf opaque, const char* filename, int mode) +{ + FILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) + file = fopen(filename, mode_fopen); + return file; +} + +static voidpf ZCALLBACK fopen64_file_func (voidpf opaque, const void* filename, int mode) +{ + FILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) + file = fopen64((const char*)filename, mode_fopen); + return file; +} + +static uLong ZCALLBACK fread_file_func (voidpf opaque, voidpf stream, void* buf, uLong size) +{ + uLong ret; + ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + +static uLong ZCALLBACK fwrite_file_func (voidpf opaque, voidpf stream, const void* buf, uLong size) +{ + uLong ret; + ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + +static long ZCALLBACK ftell_file_func (voidpf opaque, voidpf stream) +{ + long ret; + ret = ftell((FILE *)stream); + return ret; +} + +static ZPOS64_T ZCALLBACK ftell64_file_func (voidpf opaque, voidpf stream) +{ + ZPOS64_T ret; + ret = ftello64((FILE *)stream); + return ret; +} + +static long ZCALLBACK fseek_file_func (voidpf opaque, voidpf stream, uLong offset, int origin) +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + if (fseek((FILE *)stream, offset, fseek_origin) != 0) + ret = -1; + return ret; +} + +static long ZCALLBACK fseek64_file_func (voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + + if(fseeko64((FILE *)stream, offset, fseek_origin) != 0) + ret = -1; + + return ret; +} + +static int ZCALLBACK fclose_file_func (voidpf opaque, voidpf stream) +{ + int ret; + ret = fclose((FILE *)stream); + return ret; +} + +static int ZCALLBACK ferror_file_func (voidpf opaque, voidpf stream) +{ + int ret; + ret = ferror((FILE *)stream); + return ret; +} + +void fill_fopen_filefunc (pzlib_filefunc_def) + zlib_filefunc_def* pzlib_filefunc_def; +{ + pzlib_filefunc_def->zopen_file = fopen_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell_file = ftell_file_func; + pzlib_filefunc_def->zseek_file = fseek_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} + +void fill_fopen64_filefunc (zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = fopen64_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell64_file = ftell64_file_func; + pzlib_filefunc_def->zseek64_file = fseek64_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} \ No newline at end of file diff --git a/LifeCore/minizip/ioapi.h b/LifeCore/minizip/ioapi.h new file mode 100644 index 0000000..38728e9 --- /dev/null +++ b/LifeCore/minizip/ioapi.h @@ -0,0 +1,190 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip +part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + +Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + +Modifications for Zip64 support +Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + +For more info read MiniZip_info.txt + +Changes + +Oct-2009 - Defined ZPOS64_T to fpos_t on windows and u_int64_t on linux. (might need to find a better why for this) +Oct-2009 - Change to fseeko64, ftello64 and fopen64 so large files would work on linux. +More if/def section may be needed to support other platforms +Oct-2009 - Defined fxxxx64 calls to normal fopen/ftell/fseek so they would compile on windows. +(but you should use iowin32.c for windows instead) + +*/ + +#ifndef _ZLIBIOAPI64_H +#define _ZLIBIOAPI64_H + +#if (!defined(_WIN32)) && (!defined(WIN32)) + +// Linux needs this to support file operation on files larger then 4+GB +// But might need better if/def to select just the platforms that needs them. + +#ifndef __USE_FILE_OFFSET64 +#define __USE_FILE_OFFSET64 +#endif +#ifndef __USE_LARGEFILE64 +#define __USE_LARGEFILE64 +#endif +#ifndef _LARGEFILE64_SOURCE +#define _LARGEFILE64_SOURCE +#endif +#ifndef _FILE_OFFSET_BIT +#define _FILE_OFFSET_BIT 64 +#endif +#endif + +#include +#include +#include "zlib.h" + +#if defined(USE_FILE32API) +#define fopen64 fopen +#define ftello64 ftell +#define fseeko64 fseek +#else +#ifdef _MSC_VER +#define fopen64 fopen +#if (_MSC_VER >= 1400) && (!(defined(NO_MSCVER_FILE64_FUNC))) +#define ftello64 _ftelli64 +#define fseeko64 _fseeki64 +#else // old MSC +#define ftello64 ftell +#define fseeko64 fseek +#endif +#endif +#endif + +/* +#ifndef ZPOS64_T +#ifdef _WIN32 +#define ZPOS64_T fpos_t +#else +#include +#define ZPOS64_T uint64_t +#endif +#endif +*/ + +#ifdef HAVE_MINIZIP64_CONF_H +#include "mz64conf.h" +#endif + +/* a type choosen by DEFINE */ +#ifdef HAVE_64BIT_INT_CUSTOM +typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T; +#else +#ifdef HAS_STDINT_H +#include "stdint.h" +typedef uint64_t ZPOS64_T; +#else + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef unsigned __int64 ZPOS64_T; +#else +typedef unsigned long long int ZPOS64_T; +#endif +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_FILEFUNC_SEEK_CUR (1) +#define ZLIB_FILEFUNC_SEEK_END (2) +#define ZLIB_FILEFUNC_SEEK_SET (0) + +#define ZLIB_FILEFUNC_MODE_READ (1) +#define ZLIB_FILEFUNC_MODE_WRITE (2) +#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) + +#define ZLIB_FILEFUNC_MODE_EXISTING (4) +#define ZLIB_FILEFUNC_MODE_CREATE (8) + +#ifndef ZCALLBACK +#if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) +#define ZCALLBACK CALLBACK +#else +#define ZCALLBACK +#endif +#endif + + typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); + typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); + typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); + typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); + typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); + + typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); + typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); + + /* here is the "old" 32 bits structure structure */ + typedef struct zlib_filefunc_def_s + { + open_file_func zopen_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell_file_func ztell_file; + seek_file_func zseek_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; + } zlib_filefunc_def; + + typedef ZPOS64_T (ZCALLBACK *tell64_file_func) OF((voidpf opaque, voidpf stream)); + typedef long (ZCALLBACK *seek64_file_func) OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); + typedef voidpf (ZCALLBACK *open64_file_func) OF((voidpf opaque, const void* filename, int mode)); + + typedef struct zlib_filefunc64_def_s + { + open64_file_func zopen64_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell64_file_func ztell64_file; + seek64_file_func zseek64_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; + } zlib_filefunc64_def; + + void fill_fopen64_filefunc OF((zlib_filefunc64_def* pzlib_filefunc_def)); + void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); + + /* now internal definition, only for zip.c and unzip.h */ + typedef struct zlib_filefunc64_32_def_s + { + zlib_filefunc64_def zfile_func64; + open_file_func zopen32_file; + tell_file_func ztell32_file; + seek_file_func zseek32_file; + } zlib_filefunc64_32_def; + +#define ZREAD64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zread_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) +#define ZWRITE64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zwrite_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) + //#define ZTELL64(filefunc,filestream) ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream)) + //#define ZSEEK64(filefunc,filestream,pos,mode) ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode)) +#define ZCLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zclose_file)) ((filefunc).zfile_func64.opaque,filestream)) +#define ZERROR64(filefunc,filestream) ((*((filefunc).zfile_func64.zerror_file)) ((filefunc).zfile_func64.opaque,filestream)) + + voidpf call_zopen64 OF((const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode)); + long call_zseek64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin)); + ZPOS64_T call_ztell64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream)); + + void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32); + +#define ZOPEN64(filefunc,filename,mode) (call_zopen64((&(filefunc)),(filename),(mode))) +#define ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream))) +#define ZSEEK64(filefunc,filestream,pos,mode) (call_zseek64((&(filefunc)),(filestream),(pos),(mode))) + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/LifeCore/minizip/iowin32.c b/LifeCore/minizip/iowin32.c new file mode 100644 index 0000000..a666c32 --- /dev/null +++ b/LifeCore/minizip/iowin32.c @@ -0,0 +1,380 @@ +/* iowin32.c -- IO base function header for compress/uncompress .zip +Version 1.1, February 14h, 2010 +part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + +Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + +Modifications for Zip64 support +Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + +For more info read MiniZip_info.txt + +*/ + +#include + +#include "zlib.h" +#include "ioapi.h" +#include "iowin32.h" + +#ifndef INVALID_HANDLE_VALUE +#define INVALID_HANDLE_VALUE (0xFFFFFFFF) +#endif + +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + +voidpf ZCALLBACK win32_open_file_func OF((voidpf opaque, const char* filename, int mode)); +uLong ZCALLBACK win32_read_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +uLong ZCALLBACK win32_write_file_func OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); +ZPOS64_T ZCALLBACK win32_tell64_file_func OF((voidpf opaque, voidpf stream)); +long ZCALLBACK win32_seek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); +int ZCALLBACK win32_close_file_func OF((voidpf opaque, voidpf stream)); +int ZCALLBACK win32_error_file_func OF((voidpf opaque, voidpf stream)); + +typedef struct +{ + HANDLE hf; + int error; +} WIN32FILE_IOWIN; + +static void win32_translate_open_mode(int mode, + DWORD* lpdwDesiredAccess, + DWORD* lpdwCreationDisposition, + DWORD* lpdwShareMode, + DWORD* lpdwFlagsAndAttributes) +{ + *lpdwDesiredAccess = *lpdwShareMode = *lpdwFlagsAndAttributes = *lpdwCreationDisposition = 0; + + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + { + *lpdwDesiredAccess = GENERIC_READ; + *lpdwCreationDisposition = OPEN_EXISTING; + *lpdwShareMode = FILE_SHARE_READ; + } + else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + { + *lpdwDesiredAccess = GENERIC_WRITE | GENERIC_READ; + *lpdwCreationDisposition = OPEN_EXISTING; + } + else if (mode & ZLIB_FILEFUNC_MODE_CREATE) + { + *lpdwDesiredAccess = GENERIC_WRITE | GENERIC_READ; + *lpdwCreationDisposition = CREATE_ALWAYS; + } +} + +static voidpf win32_build_iowin(HANDLE hFile) +{ + voidpf ret=NULL; + + if ((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) + { + WIN32FILE_IOWIN w32fiow; + w32fiow.hf = hFile; + w32fiow.error = 0; + ret = malloc(sizeof(WIN32FILE_IOWIN)); + + if (ret==NULL) + CloseHandle(hFile); + else + *((WIN32FILE_IOWIN*)ret) = w32fiow; + } + return ret; +} + +voidpf ZCALLBACK win32_open64_file_func (voidpf opaque,const void* filename,int mode) +{ + const char* mode_fopen = NULL; + DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; + HANDLE hFile = NULL; + + win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); + + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); + + return win32_build_iowin(hFile); +} + +voidpf ZCALLBACK win32_open64_file_funcA (voidpf opaque,const void* filename,int mode) +{ + const char* mode_fopen = NULL; + DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; + HANDLE hFile = NULL; + + win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); + + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFileA((LPCSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); + + return win32_build_iowin(hFile); +} + +voidpf ZCALLBACK win32_open64_file_funcW (voidpf opaque,const void* filename,int mode) +{ + const char* mode_fopen = NULL; + DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; + HANDLE hFile = NULL; + + win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); + + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFileW((LPCWSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); + + return win32_build_iowin(hFile); +} + +voidpf ZCALLBACK win32_open_file_func (voidpf opaque,const char* filename,int mode) +{ + const char* mode_fopen = NULL; + DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; + HANDLE hFile = NULL; + + win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); + + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); + + return win32_build_iowin(hFile); +} + +uLong ZCALLBACK win32_read_file_func (voidpf opaque, voidpf stream, void* buf,uLong size) +{ + uLong ret=0; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + + if (hFile != NULL) + { + if (!ReadFile(hFile, buf, size, &ret, NULL)) + { + DWORD dwErr = GetLastError(); + if (dwErr == ERROR_HANDLE_EOF) + dwErr = 0; + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + } + } + + return ret; +} + +uLong ZCALLBACK win32_write_file_func (voidpf opaque,voidpf stream,const void* buf,uLong size) +{ + uLong ret=0; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + + if (hFile != NULL) + { + if (!WriteFile(hFile, buf, size, &ret, NULL)) + { + DWORD dwErr = GetLastError(); + if (dwErr == ERROR_HANDLE_EOF) + dwErr = 0; + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + } + } + + return ret; +} + +long ZCALLBACK win32_tell_file_func (voidpf opaque,voidpf stream) +{ + long ret=-1; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + if (hFile != NULL) + { + DWORD dwSet = SetFilePointer(hFile, 0, NULL, FILE_CURRENT); + if (dwSet == INVALID_SET_FILE_POINTER) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = -1; + } + else + ret=(long)dwSet; + } + return ret; +} + +ZPOS64_T ZCALLBACK win32_tell64_file_func (voidpf opaque, voidpf stream) +{ + ZPOS64_T ret= (ZPOS64_T)-1; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream)->hf; + + if (hFile) + { + LARGE_INTEGER li; + li.QuadPart = 0; + li.u.LowPart = SetFilePointer(hFile, li.u.LowPart, &li.u.HighPart, FILE_CURRENT); + if ( (li.LowPart == 0xFFFFFFFF) && (GetLastError() != NO_ERROR)) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = (ZPOS64_T)-1; + } + else + ret=li.QuadPart; + } + return ret; +} + +long ZCALLBACK win32_seek_file_func (voidpf opaque,voidpf stream,uLong offset,int origin) +{ + DWORD dwMoveMethod=0xFFFFFFFF; + HANDLE hFile = NULL; + + long ret=-1; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + dwMoveMethod = FILE_CURRENT; + break; + case ZLIB_FILEFUNC_SEEK_END : + dwMoveMethod = FILE_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + dwMoveMethod = FILE_BEGIN; + break; + default: return -1; + } + + if (hFile != NULL) + { + DWORD dwSet = SetFilePointer(hFile, offset, NULL, dwMoveMethod); + if (dwSet == INVALID_SET_FILE_POINTER) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = -1; + } + else + ret=0; + } + return ret; +} + +long ZCALLBACK win32_seek64_file_func (voidpf opaque, voidpf stream,ZPOS64_T offset,int origin) +{ + DWORD dwMoveMethod=0xFFFFFFFF; + HANDLE hFile = NULL; + long ret=-1; + + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream)->hf; + + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + dwMoveMethod = FILE_CURRENT; + break; + case ZLIB_FILEFUNC_SEEK_END : + dwMoveMethod = FILE_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + dwMoveMethod = FILE_BEGIN; + break; + default: return -1; + } + + if (hFile) + { + LARGE_INTEGER* li = (LARGE_INTEGER*)&offset; + DWORD dwSet = SetFilePointer(hFile, li->u.LowPart, &li->u.HighPart, dwMoveMethod); + if (dwSet == INVALID_SET_FILE_POINTER) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = -1; + } + else + ret=0; + } + return ret; +} + +int ZCALLBACK win32_close_file_func (voidpf opaque, voidpf stream) +{ + int ret=-1; + + if (stream!=NULL) + { + HANDLE hFile; + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + if (hFile != NULL) + { + CloseHandle(hFile); + ret=0; + } + free(stream); + } + return ret; +} + +int ZCALLBACK win32_error_file_func (voidpf opaque,voidpf stream) +{ + int ret=-1; + if (stream!=NULL) + { + ret = ((WIN32FILE_IOWIN*)stream) -> error; + } + return ret; +} + +void fill_win32_filefunc (zlib_filefunc_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen_file = win32_open_file_func; + pzlib_filefunc_def->zread_file = win32_read_file_func; + pzlib_filefunc_def->zwrite_file = win32_write_file_func; + pzlib_filefunc_def->ztell_file = win32_tell_file_func; + pzlib_filefunc_def->zseek_file = win32_seek_file_func; + pzlib_filefunc_def->zclose_file = win32_close_file_func; + pzlib_filefunc_def->zerror_file = win32_error_file_func; + pzlib_filefunc_def->opaque = NULL; +} + +void fill_win32_filefunc64(zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = win32_open64_file_func; + pzlib_filefunc_def->zread_file = win32_read_file_func; + pzlib_filefunc_def->zwrite_file = win32_write_file_func; + pzlib_filefunc_def->ztell64_file = win32_tell64_file_func; + pzlib_filefunc_def->zseek64_file = win32_seek64_file_func; + pzlib_filefunc_def->zclose_file = win32_close_file_func; + pzlib_filefunc_def->zerror_file = win32_error_file_func; + pzlib_filefunc_def->opaque = NULL; +} + +void fill_win32_filefunc64A(zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = win32_open64_file_funcA; + pzlib_filefunc_def->zread_file = win32_read_file_func; + pzlib_filefunc_def->zwrite_file = win32_write_file_func; + pzlib_filefunc_def->ztell64_file = win32_tell64_file_func; + pzlib_filefunc_def->zseek64_file = win32_seek64_file_func; + pzlib_filefunc_def->zclose_file = win32_close_file_func; + pzlib_filefunc_def->zerror_file = win32_error_file_func; + pzlib_filefunc_def->opaque = NULL; +} + +void fill_win32_filefunc64W(zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = win32_open64_file_funcW; + pzlib_filefunc_def->zread_file = win32_read_file_func; + pzlib_filefunc_def->zwrite_file = win32_write_file_func; + pzlib_filefunc_def->ztell64_file = win32_tell64_file_func; + pzlib_filefunc_def->zseek64_file = win32_seek64_file_func; + pzlib_filefunc_def->zclose_file = win32_close_file_func; + pzlib_filefunc_def->zerror_file = win32_error_file_func; + pzlib_filefunc_def->opaque = NULL; +} \ No newline at end of file diff --git a/LifeCore/minizip/iowin32.h b/LifeCore/minizip/iowin32.h new file mode 100644 index 0000000..c87799e --- /dev/null +++ b/LifeCore/minizip/iowin32.h @@ -0,0 +1,27 @@ +/* iowin32.h -- IO base function header for compress/uncompress .zip +Version 1.1, February 14h, 2010 +part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + +Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + +Modifications for Zip64 support +Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + +For more info read MiniZip_info.txt + +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + void fill_win32_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); + void fill_win32_filefunc64 OF((zlib_filefunc64_def* pzlib_filefunc_def)); + void fill_win32_filefunc64A OF((zlib_filefunc64_def* pzlib_filefunc_def)); + void fill_win32_filefunc64W OF((zlib_filefunc64_def* pzlib_filefunc_def)); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/LifeCore/minizip/miniunz.c b/LifeCore/minizip/miniunz.c new file mode 100644 index 0000000..263ca17 --- /dev/null +++ b/LifeCore/minizip/miniunz.c @@ -0,0 +1,638 @@ +/* + miniunz.c + Version 1.1, February 14h, 2010 + sample part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + +#ifndef _WIN32 + #ifndef __USE_FILE_OFFSET64 + #define __USE_FILE_OFFSET64 + #endif + #ifndef __USE_LARGEFILE64 + #define __USE_LARGEFILE64 + #endif + #ifndef _LARGEFILE64_SOURCE + #define _LARGEFILE64_SOURCE + #endif + #ifndef _FILE_OFFSET_BIT + #define _FILE_OFFSET_BIT 64 + #endif +#endif +#define _CRT_SECURE_NO_WARNINGS +#include +#include +#include +#include +#include +#include + +#ifdef unix +# include +# include +#else +# include +# include +#endif + +#include "unzip.h" + +#define CASESENSITIVITY (0) +#define WRITEBUFFERSIZE (8192) +#define MAXFILENAME (256) + +#ifdef _WIN32 +#define USEWIN32IOAPI +#include "iowin32.h" +#endif + mini unzip, demo of unzip package + + usage : + Usage : miniunz [-exvlo] file.zip [file_to_extract] [-d extractdir] + + list the file in the zipfile, and print the content of FILE_ID.ZIP or README.TXT + if it exists + + + change_file_date : change the date/time of a file + filename : the filename of the file where date/time must be modified + dosdate : the new date at the MSDos format (4 bytes) + tmu_date : the SAME new date at the tm_unz format void change_file_date(filename,dosdate,tmu_date) + const char *filename; + uLong dosdate; + tm_unz tmu_date; +{ +#ifdef _WIN32 + HANDLE hFile; + FILETIME ftm,ftLocal,ftCreate,ftLastAcc,ftLastWrite; + + hFile = CreateFileA(filename,GENERIC_READ | GENERIC_WRITE, + 0,NULL,OPEN_EXISTING,0,NULL); + GetFileTime(hFile,&ftCreate,&ftLastAcc,&ftLastWrite); + DosDateTimeToFileTime((WORD)(dosdate>>16),(WORD)dosdate,&ftLocal); + LocalFileTimeToFileTime(&ftLocal,&ftm); + SetFileTime(hFile,&ftm,&ftLastAcc,&ftm); + CloseHandle(hFile); +#else +#ifdef unix + struct utimbuf ut; + struct tm newdate; + newdate.tm_sec = tmu_date.tm_sec; + newdate.tm_min=tmu_date.tm_min; + newdate.tm_hour=tmu_date.tm_hour; + newdate.tm_mday=tmu_date.tm_mday; + newdate.tm_mon=tmu_date.tm_mon; + if (tmu_date.tm_year > 1900) + newdate.tm_year=tmu_date.tm_year - 1900; + else + newdate.tm_year=tmu_date.tm_year ; + newdate.tm_isdst=-1; + + ut.actime=ut.modtime=mktime(&newdate); + utime(filename,&ut); +#endif +#endif +} + + + mymkdir and change_file_date are not 100 % portable + As I don't know well Unix, I wait feedback for the unix portion +int mymkdir(dirname) + const char* dirname; +{ + int ret=0; +#ifdef _WIN32 + ret = _mkdir(dirname); +#else +#ifdef unix + ret = mkdir (dirname,0775); +#endif +#endif + return ret; +} + +int makedir (newdir) + char *newdir; +{ + char *buffer ; + char *p; + int len = (int)strlen(newdir); + + if (len <= 0) + return 0; + + buffer = (char*)malloc(len+1); + if (buffer==NULL) + { + printf("Error allocating memory\n"); + return UNZ_INTERNALERROR; + } + strcpy(buffer,newdir); + + if (buffer[len-1] == '/') { + buffer[len-1] = '\0'; + } + if (mymkdir(buffer) == 0) + { + free(buffer); + return 1; + } + + p = buffer+1; + while (1) + { + char hold; + + while(*p && *p != '\\' && *p != '/') + p++; + hold = *p; + *p = 0; + if ((mymkdir(buffer) == -1) && (errno == ENOENT)) + { + printf("couldn't create directory %s\n",buffer); + free(buffer); + return 0; + } + if (hold == 0) + break; + *p++ = hold; + } + free(buffer); + return 1; +} + +void do_banner() +{ + printf("MiniUnz 1.01b, demo of zLib + Unz package written by Gilles Vollant\n"); + printf("more info at http://www.winimage.com/zLibDll/unzip.html\n\n"); +} + +void do_help() +{ + printf("Usage : miniunz [-e] [-x] [-v] [-l] [-o] [-p password] file.zip [file_to_extr.] [-d extractdir]\n\n" \ + " -e Extract without pathname (junk paths)\n" \ + " -x Extract with pathname\n" \ + " -v list files\n" \ + " -l list files\n" \ + " -d directory to extract into\n" \ + " -o overwrite files without prompting\n" \ + " -p extract crypted file using password\n\n"); +} + +void Display64BitsSize(ZPOS64_T n, int size_char) +{ + to avoid compatibility problem , we do here the conversion char number[21]; + int offset=19; + int pos_string = 19; + number[20]=0; + for (;;) { + number[offset]=(char)((n%10)+'0'); + if (number[offset] != '0') + pos_string=offset; + n/=10; + if (offset==0) + break; + offset--; + } + { + int size_display_string = 19-pos_string; + while (size_char > size_display_string) + { + size_char--; + printf(" "); + } + } + + printf("%s",&number[pos_string]); +} + +int do_list(uf) + unzFile uf; +{ + uLong i; + unz_global_info64 gi; + int err; + + err = unzGetGlobalInfo64(uf,&gi); + if (err!=UNZ_OK) + printf("error %d with zipfile in unzGetGlobalInfo \n",err); + printf(" Length Method Size Ratio Date Time CRC-32 Name\n"); + printf(" ------ ------ ---- ----- ---- ---- ------ ----\n"); + for (i=0;i0) + ratio = (uLong)((file_info.compressed_size*100)/file_info.uncompressed_size); + + display a '*' if the file is crypted if ((file_info.flag & 1) != 0) + charCrypt='*'; + + if (file_info.compression_method==0) + string_method="Stored"; + else + if (file_info.compression_method==Z_DEFLATED) + { + uInt iLevel=(uInt)((file_info.flag & 0x6)/2); + if (iLevel==0) + string_method="Defl:N"; + else if (iLevel==1) + string_method="Defl:X"; + else if ((iLevel==2) || (iLevel==3)) + string_method="Defl:F"; 2:fast , 3 : extra fast } + else + if (file_info.compression_method==Z_BZIP2ED) + { + string_method="BZip2 "; + } + else + string_method="Unkn. "; + + Display64BitsSize(file_info.uncompressed_size,7); + printf(" %6s%c",string_method,charCrypt); + Display64BitsSize(file_info.compressed_size,7); + printf(" %3lu%% %2.2lu-%2.2lu-%2.2lu %2.2lu:%2.2lu %8.8lx %s\n", + ratio, + (uLong)file_info.tmu_date.tm_mon + 1, + (uLong)file_info.tmu_date.tm_mday, + (uLong)file_info.tmu_date.tm_year % 100, + (uLong)file_info.tmu_date.tm_hour,(uLong)file_info.tmu_date.tm_min, + (uLong)file_info.crc,filename_inzip); + if ((i+1)='a') && (rep<='z')) + rep -= 0x20; + } + while ((rep!='Y') && (rep!='N') && (rep!='A')); + } + + if (rep == 'N') + skip = 1; + + if (rep == 'A') + *popt_overwrite=1; + } + + if ((skip==0) && (err==UNZ_OK)) + { + fout=fopen64(write_filename,"wb"); + + some zipfile don't contain directory alone before file if ((fout==NULL) && ((*popt_extract_without_path)==0) && + (filename_withoutpath!=(char*)filename_inzip)) + { + char c=*(filename_withoutpath-1); + *(filename_withoutpath-1)='\0'; + makedir(write_filename); + *(filename_withoutpath-1)=c; + fout=fopen64(write_filename,"wb"); + } + + if (fout==NULL) + { + printf("error opening %s\n",write_filename); + } + } + + if (fout!=NULL) + { + printf(" extracting: %s\n",write_filename); + + do + { + err = unzReadCurrentFile(uf,buf,size_buf); + if (err<0) + { + printf("error %d with zipfile in unzReadCurrentFile\n",err); + break; + } + if (err>0) + if (fwrite(buf,err,1,fout)!=1) + { + printf("error in writing extracted file\n"); + err=UNZ_ERRNO; + break; + } + } + while (err>0); + if (fout) + fclose(fout); + + if (err==0) + change_file_date(write_filename,file_info.dosDate, + file_info.tmu_date); + } + + if (err==UNZ_OK) + { + err = unzCloseCurrentFile (uf); + if (err!=UNZ_OK) + { + printf("error %d with zipfile in unzCloseCurrentFile\n",err); + } + } + else + unzCloseCurrentFile(uf); don't lose the error } + + free(buf); + return err; +} + + +int do_extract(uf,opt_extract_without_path,opt_overwrite,password) + unzFile uf; + int opt_extract_without_path; + int opt_overwrite; + const char* password; +{ + uLong i; + unz_global_info64 gi; + int err; + FILE* fout=NULL; + + err = unzGetGlobalInfo64(uf,&gi); + if (err!=UNZ_OK) + printf("error %d with zipfile in unzGetGlobalInfo \n",err); + + for (i=0;i +#include +#include +#include +#include +#include + +#ifdef unix +# include +# include +# include +# include +#else +# include +# include +#endif + +#include "zip.h" + +#ifdef _WIN32 + #define USEWIN32IOAPI + #include "iowin32.h" +#endif + + + +#define WRITEBUFFERSIZE (16384) +#define MAXFILENAME (256) + +#ifdef _WIN32 +uLong filetime(f, tmzip, dt) + char *f; name of file to get info on tm_zip *tmzip; return value: access, modific. and creation times uLong *dt; dostime { + int ret = 0; + { + FILETIME ftLocal; + HANDLE hFind; + WIN32_FIND_DATAA ff32; + + hFind = FindFirstFileA(f,&ff32); + if (hFind != INVALID_HANDLE_VALUE) + { + FileTimeToLocalFileTime(&(ff32.ftLastWriteTime),&ftLocal); + FileTimeToDosDateTime(&ftLocal,((LPWORD)dt)+1,((LPWORD)dt)+0); + FindClose(hFind); + ret = 1; + } + } + return ret; +} +#else +#ifdef unix +uLong filetime(f, tmzip, dt) + char *f; name of file to get info on tm_zip *tmzip; return value: access, modific. and creation times uLong *dt; dostime { + int ret=0; + struct stat s; results of stat() struct tm* filedate; + time_t tm_t=0; + + if (strcmp(f,"-")!=0) + { + char name[MAXFILENAME+1]; + int len = strlen(f); + if (len > MAXFILENAME) + len = MAXFILENAME; + + strncpy(name, f,MAXFILENAME-1); + strncpy doesnt append the trailing NULL, of the string is too long. name[ MAXFILENAME ] = '\0'; + + if (name[len - 1] == '/') + name[len - 1] = '\0'; + not all systems allow stat'ing a file with / appended if (stat(name,&s)==0) + { + tm_t = s.st_mtime; + ret = 1; + } + } + filedate = localtime(&tm_t); + + tmzip->tm_sec = filedate->tm_sec; + tmzip->tm_min = filedate->tm_min; + tmzip->tm_hour = filedate->tm_hour; + tmzip->tm_mday = filedate->tm_mday; + tmzip->tm_mon = filedate->tm_mon ; + tmzip->tm_year = filedate->tm_year; + + return ret; +} +#else +uLong filetime(f, tmzip, dt) + char *f; name of file to get info on tm_zip *tmzip; return value: access, modific. and creation times uLong *dt; dostime { + return 0; +} +#endif +#endif + + + + +int check_exist_file(filename) + const char* filename; +{ + FILE* ftestexist; + int ret = 1; + ftestexist = fopen64(filename,"rb"); + if (ftestexist==NULL) + ret = 0; + else + fclose(ftestexist); + return ret; +} + +void do_banner() +{ + printf("MiniZip 1.1, demo of zLib + MiniZip64 package, written by Gilles Vollant\n"); + printf("more info on MiniZip at http://www.winimage.com/zLibDll/minizip.html\n\n"); +} + +void do_help() +{ + printf("Usage : minizip [-o] [-a] [-0 to -9] [-p password] [-j] file.zip [files_to_add]\n\n" \ + " -o Overwrite existing file.zip\n" \ + " -a Append to existing file.zip\n" \ + " -0 Store only\n" \ + " -1 Compress faster\n" \ + " -9 Compress better\n\n" \ + " -j exclude path. store only the file name.\n\n"); +} + + calculate the CRC32 of a file, + because to encrypt a file, we need known the CRC32 of the file before int getFileCrc(const char* filenameinzip,void*buf,unsigned long size_buf,unsigned long* result_crc) +{ + unsigned long calculate_crc=0; + int err=ZIP_OK; + FILE * fin = fopen64(filenameinzip,"rb"); + unsigned long size_read = 0; + unsigned long total_read = 0; + if (fin==NULL) + { + err = ZIP_ERRNO; + } + + if (err == ZIP_OK) + do + { + err = ZIP_OK; + size_read = (int)fread(buf,1,size_buf,fin); + if (size_read < size_buf) + if (feof(fin)==0) + { + printf("error in reading %s\n",filenameinzip); + err = ZIP_ERRNO; + } + + if (size_read>0) + calculate_crc = crc32(calculate_crc,buf,size_read); + total_read += size_read; + + } while ((err == ZIP_OK) && (size_read>0)); + + if (fin) + fclose(fin); + + *result_crc=calculate_crc; + printf("file %s crc %lx\n", filenameinzip, calculate_crc); + return err; +} + +int isLargeFile(const char* filename) +{ + int largeFile = 0; + ZPOS64_T pos = 0; + FILE* pFile = fopen64(filename, "rb"); + + if(pFile != NULL) + { + int n = fseeko64(pFile, 0, SEEK_END); + + pos = ftello64(pFile); + + printf("File : %s is %lld bytes\n", filename, pos); + + if(pos >= 0xffffffff) + largeFile = 1; + + fclose(pFile); + } + + return largeFile; +} + +int main(argc,argv) + int argc; + char *argv[]; +{ + int i; + int opt_overwrite=0; + int opt_compress_level=Z_DEFAULT_COMPRESSION; + int opt_exclude_path=0; + int zipfilenamearg = 0; + char filename_try[MAXFILENAME+16]; + int zipok; + int err=0; + int size_buf=0; + void* buf=NULL; + const char* password=NULL; + + + do_banner(); + if (argc==1) + { + do_help(); + return 0; + } + else + { + for (i=1;i='0') && (c<='9')) + opt_compress_level = c-'0'; + if ((c=='j') || (c=='J')) + opt_exclude_path = 1; + + if (((c=='p') || (c=='P')) && (i+1='a') && (rep<='z')) + rep -= 0x20; + } + while ((rep!='Y') && (rep!='N') && (rep!='A')); + if (rep=='N') + zipok = 0; + if (rep=='A') + opt_overwrite = 2; + } + } + + if (zipok==1) + { + zipFile zf; + int errclose; +# ifdef USEWIN32IOAPI + zlib_filefunc64_def ffunc; + fill_win32_filefunc64A(&ffunc); + zf = zipOpen2_64(filename_try,(opt_overwrite==2) ? 2 : 0,NULL,&ffunc); +# else + zf = zipOpen64(filename_try,(opt_overwrite==2) ? 2 : 0); +# endif + + if (zf == NULL) + { + printf("error opening %s\n",filename_try); + err= ZIP_ERRNO; + } + else + printf("creating %s\n",filename_try); + + for (i=zipfilenamearg+1;(i='0') || (argv[i][1]<='9'))) && + (strlen(argv[i]) == 2))) + { + FILE * fin; + int size_read; + const char* filenameinzip = argv[i]; + const char *savefilenameinzip; + zip_fileinfo zi; + unsigned long crcFile=0; + int zip64 = 0; + + zi.tmz_date.tm_sec = zi.tmz_date.tm_min = zi.tmz_date.tm_hour = + zi.tmz_date.tm_mday = zi.tmz_date.tm_mon = zi.tmz_date.tm_year = 0; + zi.dosDate = 0; + zi.internal_fa = 0; + zi.external_fa = 0; + filetime(filenameinzip,&zi.tmz_date,&zi.dosDate); + + err = zipOpenNewFileInZip(zf,filenameinzip,&zi, + NULL,0,NULL,0,NULL / * comment * /, + (opt_compress_level != 0) ? Z_DEFLATED : 0, + opt_compress_level); + if ((password != NULL) && (err==ZIP_OK)) + err = getFileCrc(filenameinzip,buf,size_buf,&crcFile); + + zip64 = isLargeFile(filenameinzip); + + The path name saved, should not include a leading slash. if it did, windows/xp and dynazip couldn't read the zip file. savefilenameinzip = filenameinzip; + while( savefilenameinzip[0] == '\\' || savefilenameinzip[0] == '/' ) + { + savefilenameinzip++; + } + + should the zip file contain any path at all? if( opt_exclude_path ) + { + const char *tmpptr; + const char *lastslash = 0; + for( tmpptr = savefilenameinzip; *tmpptr; tmpptr++) + { + if( *tmpptr == '\\' || *tmpptr == '/') + { + lastslash = tmpptr; + } + } + if( lastslash != NULL ) + { + savefilenameinzip = lastslash+1; // base filename follows last slash. + } + } + + err = zipOpenNewFileInZip3_64(zf,savefilenameinzip,&zi, + NULL,0,NULL,0,NULL comment, + (opt_compress_level != 0) ? Z_DEFLATED : 0, + opt_compress_level,0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + password,crcFile, zip64); + + if (err != ZIP_OK) + printf("error in opening %s in zipfile\n",filenameinzip); + else + { + fin = fopen64(filenameinzip,"rb"); + if (fin==NULL) + { + err=ZIP_ERRNO; + printf("error in opening %s for reading\n",filenameinzip); + } + } + + if (err == ZIP_OK) + do + { + err = ZIP_OK; + size_read = (int)fread(buf,1,size_buf,fin); + if (size_read < size_buf) + if (feof(fin)==0) + { + printf("error in reading %s\n",filenameinzip); + err = ZIP_ERRNO; + } + + if (size_read>0) + { + err = zipWriteInFileInZip (zf,buf,size_read); + if (err<0) + { + printf("error in writing %s in the zipfile\n", + filenameinzip); + } + + } + } while ((err == ZIP_OK) && (size_read>0)); + + if (fin) + fclose(fin); + + if (err<0) + err=ZIP_ERRNO; + else + { + err = zipCloseFileInZip(zf); + if (err!=ZIP_OK) + printf("error in closing %s in the zipfile\n", + filenameinzip); + } + } + } + errclose = zipClose(zf,NULL); + if (errclose != ZIP_OK) + printf("error in closing %s\n",filename_try); + } + else + { + do_help(); + } + + free(buf); + return 0; +} +*/ diff --git a/LifeCore/minizip/mztools.c b/LifeCore/minizip/mztools.c new file mode 100644 index 0000000..0a62d59 --- /dev/null +++ b/LifeCore/minizip/mztools.c @@ -0,0 +1,276 @@ +/* +Additional tools for Minizip +Code: Xavier Roche '2004 +License: Same as ZLIB (www.gzip.org) +*/ +#define _CRT_SECURE_NO_WARNINGS +/* Code */ +#include +#include +#include +#include "zlib.h" +#include "unzip.h" + +#define READ_8(adr) ((unsigned char)*(adr)) +#define READ_16(adr) ( READ_8(adr) | (READ_8(adr+1) << 8) ) +#define READ_32(adr) ( READ_16(adr) | (READ_16((adr)+2) << 16) ) + +#define WRITE_8(buff, n) do { \ + *((unsigned char*)(buff)) = (unsigned char) ((n) & 0xff); \ +} while(0) +#define WRITE_16(buff, n) do { \ + WRITE_8((unsigned char*)(buff), n); \ + WRITE_8(((unsigned char*)(buff)) + 1, (n) >> 8); \ +} while(0) +#define WRITE_32(buff, n) do { \ + WRITE_16((unsigned char*)(buff), (n) & 0xffff); \ + WRITE_16((unsigned char*)(buff) + 2, (n) >> 16); \ +} while(0) + +extern int ZEXPORT unzRepair(file, fileOut, fileOutTmp, nRecovered, bytesRecovered) + const char* file; +const char* fileOut; +const char* fileOutTmp; +uLong* nRecovered; +uLong* bytesRecovered; +{ + int err = Z_OK; + FILE* fpZip = fopen(file, "rb"); + FILE* fpOut = fopen(fileOut, "wb"); + FILE* fpOutCD = fopen(fileOutTmp, "wb"); + if (fpZip != NULL && fpOut != NULL) { + int entries = 0; + uLong totalBytes = 0; + char header[30]; + char filename[256]; + char extra[1024]; + int offset = 0; + int offsetCD = 0; + while ( fread(header, 1, 30, fpZip) == 30 ) { + int currentOffset = offset; + + /* File entry */ + if (READ_32(header) == 0x04034b50) { + unsigned int version = READ_16(header + 4); + unsigned int gpflag = READ_16(header + 6); + unsigned int method = READ_16(header + 8); + unsigned int filetime = READ_16(header + 10); + unsigned int filedate = READ_16(header + 12); + unsigned int crc = READ_32(header + 14); /* crc */ + unsigned int cpsize = READ_32(header + 18); /* compressed size */ + unsigned int uncpsize = READ_32(header + 22); /* uncompressed sz */ + unsigned int fnsize = READ_16(header + 26); /* file name length */ + unsigned int extsize = READ_16(header + 28); /* extra field length */ + filename[0] = extra[0] = '\0'; + + /* Header */ + if (fwrite(header, 1, 30, fpOut) == 30) { + offset += 30; + } else { + err = Z_ERRNO; + break; + } + + /* Filename */ + if (fnsize > 0) { + if (fread(filename, 1, fnsize, fpZip) == fnsize) { + if (fwrite(filename, 1, fnsize, fpOut) == fnsize) { + offset += fnsize; + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_STREAM_ERROR; + break; + } + + /* Extra field */ + if (extsize > 0) { + if (fread(extra, 1, extsize, fpZip) == extsize) { + if (fwrite(extra, 1, extsize, fpOut) == extsize) { + offset += extsize; + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_ERRNO; + break; + } + } + + /* Data */ + { + int dataSize = cpsize; + if (dataSize == 0) { + dataSize = uncpsize; + } + if (dataSize > 0) { + char* data = malloc(dataSize); + if (data != NULL) { + if ((int)fread(data, 1, dataSize, fpZip) == dataSize) { + if ((int)fwrite(data, 1, dataSize, fpOut) == dataSize) { + offset += dataSize; + totalBytes += dataSize; + } else { + err = Z_ERRNO; + } + } else { + err = Z_ERRNO; + } + free(data); + if (err != Z_OK) { + break; + } + } else { + err = Z_MEM_ERROR; + break; + } + } + } + + /* Central directory entry */ + { + char header[46]; + char* comment = ""; + int comsize = (int) strlen(comment); + WRITE_32(header, 0x02014b50); + WRITE_16(header + 4, version); + WRITE_16(header + 6, version); + WRITE_16(header + 8, gpflag); + WRITE_16(header + 10, method); + WRITE_16(header + 12, filetime); + WRITE_16(header + 14, filedate); + WRITE_32(header + 16, crc); + WRITE_32(header + 20, cpsize); + WRITE_32(header + 24, uncpsize); + WRITE_16(header + 28, fnsize); + WRITE_16(header + 30, extsize); + WRITE_16(header + 32, comsize); + WRITE_16(header + 34, 0); /* disk # */ + WRITE_16(header + 36, 0); /* int attrb */ + WRITE_32(header + 38, 0); /* ext attrb */ + WRITE_32(header + 42, currentOffset); + /* Header */ + if (fwrite(header, 1, 46, fpOutCD) == 46) { + offsetCD += 46; + + /* Filename */ + if (fnsize > 0) { + if (fwrite(filename, 1, fnsize, fpOutCD) == fnsize) { + offsetCD += fnsize; + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_STREAM_ERROR; + break; + } + + /* Extra field */ + if (extsize > 0) { + if (fwrite(extra, 1, extsize, fpOutCD) == extsize) { + offsetCD += extsize; + } else { + err = Z_ERRNO; + break; + } + } + + /* Comment field */ + if (comsize > 0) { + if ((int)fwrite(comment, 1, comsize, fpOutCD) == comsize) { + offsetCD += comsize; + } else { + err = Z_ERRNO; + break; + } + } + } else { + err = Z_ERRNO; + break; + } + } + + /* Success */ + entries++; + } else { + break; + } + } + + /* Final central directory */ + { + int entriesZip = entries; + char header[22]; + char* comment = ""; // "ZIP File recovered by zlib/minizip/mztools"; + int comsize = (int) strlen(comment); + if (entriesZip > 0xffff) { + entriesZip = 0xffff; + } + WRITE_32(header, 0x06054b50); + WRITE_16(header + 4, 0); /* disk # */ + WRITE_16(header + 6, 0); /* disk # */ + WRITE_16(header + 8, entriesZip); /* hack */ + WRITE_16(header + 10, entriesZip); /* hack */ + WRITE_32(header + 12, offsetCD); /* size of CD */ + WRITE_32(header + 16, offset); /* offset to CD */ + WRITE_16(header + 20, comsize); /* comment */ + + /* Header */ + if (fwrite(header, 1, 22, fpOutCD) == 22) { + /* Comment field */ + if (comsize > 0) { + if ((int)fwrite(comment, 1, comsize, fpOutCD) != comsize) { + err = Z_ERRNO; + } + } + } else { + err = Z_ERRNO; + } + } + + /* Final merge (file + central directory) */ + fclose(fpOutCD); + if (err == Z_OK) { + fpOutCD = fopen(fileOutTmp, "rb"); + if (fpOutCD != NULL) { + int nRead; + char buffer[8192]; + while ( (nRead = (int)fread(buffer, 1, sizeof(buffer), fpOutCD)) > 0) { + if ((int)fwrite(buffer, 1, nRead, fpOut) != nRead) { + err = Z_ERRNO; + break; + } + } + fclose(fpOutCD); + } + } + + /* Close */ + fclose(fpZip); + fclose(fpOut); + + /* Wipe temporary file */ + (void)remove(fileOutTmp); + + /* Number of recovered entries */ + if (err == Z_OK) { + if (nRecovered != NULL) { + *nRecovered = entries; + } + if (bytesRecovered != NULL) { + *bytesRecovered = totalBytes; + } + } + } else { + err = Z_STREAM_ERROR; + } + return err; +} \ No newline at end of file diff --git a/LifeCore/minizip/mztools.h b/LifeCore/minizip/mztools.h new file mode 100644 index 0000000..8e6a879 --- /dev/null +++ b/LifeCore/minizip/mztools.h @@ -0,0 +1,31 @@ +/* +Additional tools for Minizip +Code: Xavier Roche '2004 +License: Same as ZLIB (www.gzip.org) +*/ + +#ifndef _zip_tools_H +#define _zip_tools_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#include "unzip.h" + + /* Repair a ZIP file (missing central directory) + file: file to recover + fileOut: output file after recovery + fileOutTmp: temporary file name used for recovery + */ + extern int ZEXPORT unzRepair(const char* file, + const char* fileOut, + const char* fileOutTmp, + uLong* nRecovered, + uLong* bytesRecovered); + +#endif \ No newline at end of file diff --git a/LifeCore/minizip/unzip.c b/LifeCore/minizip/unzip.c new file mode 100644 index 0000000..5db50d2 --- /dev/null +++ b/LifeCore/minizip/unzip.c @@ -0,0 +1,2077 @@ +/* unzip.c -- IO for uncompress .zip files using zlib +Version 1.1, February 14h, 2010 +part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + +Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + +Modifications of Unzip for Zip64 +Copyright (C) 2007-2008 Even Rouault + +Modifications for Zip64 support on both zip and unzip +Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + +For more info read MiniZip_info.txt + +------------------------------------------------------------------------------------ +Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of +compatibility with older software. The following is from the original crypt.c. +Code woven in by Terry Thorsen 1/2003. + +Copyright (c) 1990-2000 Info-ZIP. All rights reserved. + +See the accompanying file LICENSE, version 2000-Apr-09 or later +(the contents of which are also included in zip.h) for terms of use. +If, for some reason, all these files are missing, the Info-ZIP license +also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html + +crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] + +The encryption/decryption parts of this source code (as opposed to the +non-echoing password parts) were originally written in Europe. The +whole source package can be freely distributed, including from the USA. +(Prior to January 2000, re-export from the US was a violation of US law.) + +This encryption code is a direct transcription of the algorithm from +Roger Schlafly, described by Phil Katz in the file appnote.txt. This +file (appnote.txt) is distributed with the PKZIP program (even in the +version without encryption capabilities). + +------------------------------------------------------------------------------------ + +Changes in unzip.c + +2007-2008 - Even Rouault - Addition of cpl_unzGetCurrentFileZStreamPos +2007-2008 - Even Rouault - Decoration of symbol names unz* -> cpl_unz* +2007-2008 - Even Rouault - Remove old C style function prototypes +2007-2008 - Even Rouault - Add unzip support for ZIP64 + +Copyright (C) 2007-2008 Even Rouault + +Oct-2009 - Mathias Svensson - Removed cpl_* from symbol names (Even Rouault added them but since this is now moved to a new project (minizip64) I renamed them again). +Oct-2009 - Mathias Svensson - Fixed problem if uncompressed size was > 4G and compressed size was <4G +should only read the compressed/uncompressed size from the Zip64 format if +the size from normal header was 0xFFFFFFFF +Oct-2009 - Mathias Svensson - Applied some bug fixes from paches recived from Gilles Vollant +Oct-2009 - Mathias Svensson - Applied support to unzip files with compression mathod BZIP2 (bzip2 lib is required) +Patch created by Daniel Borca + +Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer + +Copyright (C) 1998 - 2010 Gilles Vollant, Even Rouault, Mathias Svensson + +*/ + +#include +#include +#include + +#ifndef NOUNCRYPT +#define NOUNCRYPT +#endif + +#include "zlib.h" +#include "unzip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H +extern int errno; +#else +# include +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +#ifndef CASESENSITIVITYDEFAULT_NO +# if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) +# define CASESENSITIVITYDEFAULT_NO +# endif +#endif + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + +const char unz_copyright[] = + " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info64_internal_s +{ + ZPOS64_T offset_curfile;/* relative offset of local header 8 bytes */ +} unz_file_info64_internal; + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, +when reading and decompress it */ +typedef struct +{ + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + +#ifdef HAVE_BZIP2 + bz_stream bstream; /* bzLib stream structure for bziped */ +#endif + + ZPOS64_T pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + ZPOS64_T offset_local_extrafield;/* offset of the local extra field */ + uInt size_local_extrafield;/* size of the local extra field */ + ZPOS64_T pos_local_extrafield; /* position in the local extra field in read*/ + ZPOS64_T total_out_64; + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + ZPOS64_T rest_read_compressed; /* number of byte to be decompressed */ + ZPOS64_T rest_read_uncompressed;/*number of byte to be obtained after decomp*/ + zlib_filefunc64_32_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + int raw; +} file_in_zip64_read_info_s; + +/* unz64_s contain internal information about the zipfile +*/ +typedef struct +{ + zlib_filefunc64_32_def z_filefunc; + int is64bitOpenFunction; + voidpf filestream; /* io structore of the zipfile */ + unz_global_info64 gi; /* public global information */ + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + ZPOS64_T num_file; /* number of the current file in the zipfile*/ + ZPOS64_T pos_in_central_dir; /* pos of the current file in the central dir*/ + ZPOS64_T current_file_ok; /* flag about the usability of the current file*/ + ZPOS64_T central_pos; /* position of the beginning of the central dir*/ + + ZPOS64_T size_central_dir; /* size of the central directory */ + ZPOS64_T offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info64 cur_file_info; /* public info about the current file in zip*/ + unz_file_info64_internal cur_file_info_internal; /* private info about it*/ + file_in_zip64_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ + int encrypted; + + int isZip64; + +# ifndef NOUNCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const unsigned long* pcrc_32_tab; +# endif +} unz64_s; + +#ifndef NOUNCRYPT +#include "crypt.h" +#endif + +/* =========================================================================== +Read a byte from a gz_stream; update next_in and avail_in. Return EOF +for end of file. +IN assertion: the stream s has been sucessfully opened for reading. +*/ + +local int unz64local_getByte OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + int *pi)); + +local int unz64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi) +{ + unsigned char c; + int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { + if (ZERROR64(*pzlib_filefunc_def,filestream)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} + +/* =========================================================================== +Reads a long in LSB order from the given gz_stream. Sets +*/ +local int unz64local_getShort OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unz64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX) +{ + uLong x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unz64local_getLong OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unz64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX) +{ + uLong x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unz64local_getLong64 OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + ZPOS64_T *pX)); + +local int unz64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + ZPOS64_T *pX) +{ + ZPOS64_T x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (ZPOS64_T)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<8; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<16; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<24; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<32; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<40; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<48; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<56; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +/* My own strcmpi / strcasecmp */ +local int strcmpcasenosensitive_internal (const char* fileName1, const char* fileName2) +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* +Compare two filename (fileName1,fileName2). +If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) +If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi +or strcasecmp) +If iCaseSenisivity = 0, case sensitivity is defaut of your operating system +(like 1 on Unix, 2 on Windows) + +*/ +extern int ZEXPORT unzStringFileNameCompare (const char* fileName1, + const char* fileName2, + int iCaseSensitivity) + +{ + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif + +/* +Locate the Central directory of a zipfile (at the end, just before +the global comment) +*/ +local ZPOS64_T unz64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); +local ZPOS64_T unz64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* +Locate the Central directory 64 of a zipfile (at the end, just before +the global comment) +*/ +local ZPOS64_T unz64local_SearchCentralDir64 OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream)); + +local ZPOS64_T unz64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + uLong uL; + ZPOS64_T relativeOffset; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + if (uPosFound == 0) + return 0; + + /* Zip64 end of central directory locator */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature, already checked */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + + /* number of the disk with the start of the zip64 end of central directory */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + if (uL != 0) + return 0; + + /* relative offset of the zip64 end of central directory record */ + if (unz64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=UNZ_OK) + return 0; + + /* total number of disks */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + if (uL != 1) + return 0; + + /* Goto end of central directory record */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + + if (uL != 0x06064b50) + return 0; + + return relativeOffset; +} + +/* +Open a Zip file. path contain the full pathname (by example, +on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer +"zlib/zlib114.zip". +If the zipfile cannot be opened (file doesn't exist or in not valid), the +return value is NULL. +Else, the return value is a unzFile Handle, usable with other function +of this unzip package. +*/ +local unzFile unzOpenInternal (const void *path, + zlib_filefunc64_32_def* pzlib_filefunc64_32_def, + int is64bitOpenFunction) +{ + unz64_s us; + unz64_s *s; + ZPOS64_T central_pos; + uLong uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + ZPOS64_T number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + if (unz_copyright[0]!=' ') + return NULL; + + us.z_filefunc.zseek32_file = NULL; + us.z_filefunc.ztell32_file = NULL; + if (pzlib_filefunc64_32_def==NULL) + fill_fopen64_filefunc(&us.z_filefunc.zfile_func64); + else + us.z_filefunc = *pzlib_filefunc64_32_def; + us.is64bitOpenFunction = is64bitOpenFunction; + + us.filestream = ZOPEN64(us.z_filefunc, + path, + ZLIB_FILEFUNC_MODE_READ | + ZLIB_FILEFUNC_MODE_EXISTING); + if (us.filestream==NULL) + return NULL; + + central_pos = unz64local_SearchCentralDir64(&us.z_filefunc,us.filestream); + if (central_pos) + { + uLong uS; + ZPOS64_T uL64; + + us.isZip64 = 1; + + if (ZSEEK64(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* size of zip64 end of central directory record */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&uL64)!=UNZ_OK) + err=UNZ_ERRNO; + + /* version made by */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) + err=UNZ_ERRNO; + + /* version needed to extract */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central directory on this disk */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central directory */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + us.gi.size_comment = 0; + } + else + { + central_pos = unz64local_SearchCentralDir(&us.z_filefunc,us.filestream); + if (central_pos==0) + err=UNZ_ERRNO; + + us.isZip64 = 0; + + if (ZSEEK64(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.gi.number_entry = uL; + + /* total number of entries in the central dir */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + number_entry_CD = uL; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.size_central_dir = uL; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.offset_central_dir = uL; + + /* zipfile comment length */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + } + + if ((central_pospfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + ZCLOSE64(s->z_filefunc, s->filestream); + TRYFREE(s); + return UNZ_OK; +} + +/* +Write info about the ZipFile in the *pglobal_info structure. +No preparation of the structure is needed +return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzGetGlobalInfo64 (unzFile file, unz_global_info64* pglobal_info) +{ + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + +extern int ZEXPORT unzGetGlobalInfo (unzFile file, unz_global_info* pglobal_info32) +{ + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + /* to do : check if number_entry is not truncated */ + pglobal_info32->number_entry = (uLong)s->gi.number_entry; + pglobal_info32->size_comment = s->gi.size_comment; + return UNZ_OK; +} +/* +Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +local void unz64local_DosDateToTmuDate (ZPOS64_T ulDosDate, tm_unz* ptm) +{ + ZPOS64_T uDate; + uDate = (ZPOS64_T)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* +Get Info about the current file in the zipfile, with internal only info +*/ +local int unz64local_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info64 *pfile_info, + unz_file_info64_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +local int unz64local_GetCurrentFileInfoInternal (unzFile file, + unz_file_info64 *pfile_info, + unz_file_info64_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize) +{ + unz64_s* s; + unz_file_info64 file_info; + unz_file_info64_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + uLong uL; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (ZSEEK64(s->z_filefunc, s->filestream, + s->pos_in_central_dir+s->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* we check the magic */ + if (err==UNZ_OK) + { + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + } + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unz64local_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info.compressed_size = uL; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info.uncompressed_size = uL; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + // relative offset of local header + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info_internal.offset_curfile = uL; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + // Read extrafield + if ((err==UNZ_OK) && (extraField!=NULL)) + { + ZPOS64_T uSizeRead ; + if (file_info.size_file_extraz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,extraField,(uLong)uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + + lSeek += file_info.size_file_extra - (uLong)uSizeRead; + } + else + lSeek += file_info.size_file_extra; + + if ((err==UNZ_OK) && (file_info.size_file_extra != 0)) + { + uLong acc = 0; + + // since lSeek now points to after the extra field we need to move back + lSeek -= file_info.size_file_extra; + + if (lSeek!=0) + { + if (ZSEEK64(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + while(acc < file_info.size_file_extra) + { + uLong headerId; + uLong dataSize; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&headerId) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&dataSize) != UNZ_OK) + err=UNZ_ERRNO; + + /* ZIP64 extra fields */ + if (headerId == 0x0001) + { + uLong uL; + + if(file_info.uncompressed_size == (ZPOS64_T)(unsigned long)-1) + { + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info.compressed_size == (ZPOS64_T)(unsigned long)-1) + { + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info_internal.offset_curfile == (ZPOS64_T)(unsigned long)-1) + { + /* Relative Header offset */ + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info.disk_num_start == (unsigned long)-1) + { + /* Disk Start Number */ + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + } + } + else + { + if (ZSEEK64(s->z_filefunc, s->filestream,dataSize,ZLIB_FILEFUNC_SEEK_CUR)!=0) + err=UNZ_ERRNO; + } + + acc += 2 + 2 + dataSize; + } + } + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek+=file_info.size_file_comment - uSizeRead; + } + else + lSeek+=file_info.size_file_comment; + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + +/* +Write info about the ZipFile in the *pglobal_info structure. +No preparation of the structure is needed +return UNZ_OK if there is no problem. +*/ +extern int ZEXPORT unzGetCurrentFileInfo64 (unzFile file, + unz_file_info64 * pfile_info, + char * szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char* szComment, uLong commentBufferSize) +{ + return unz64local_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); +} + +extern int ZEXPORT unzGetCurrentFileInfo (unzFile file, + unz_file_info * pfile_info, + char * szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char* szComment, uLong commentBufferSize) +{ + int err; + unz_file_info64 file_info64; + err = unz64local_GetCurrentFileInfoInternal(file,&file_info64,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); + if (err==UNZ_OK) + { + pfile_info->version = file_info64.version; + pfile_info->version_needed = file_info64.version_needed; + pfile_info->flag = file_info64.flag; + pfile_info->compression_method = file_info64.compression_method; + pfile_info->dosDate = file_info64.dosDate; + pfile_info->crc = file_info64.crc; + + pfile_info->size_filename = file_info64.size_filename; + pfile_info->size_file_extra = file_info64.size_file_extra; + pfile_info->size_file_comment = file_info64.size_file_comment; + + pfile_info->disk_num_start = file_info64.disk_num_start; + pfile_info->internal_fa = file_info64.internal_fa; + pfile_info->external_fa = file_info64.external_fa; + + pfile_info->tmu_date = file_info64.tmu_date, + + pfile_info->compressed_size = (uLong)file_info64.compressed_size; + pfile_info->uncompressed_size = (uLong)file_info64.uncompressed_size; + } + return err; +} +/* +Set the current file of the zipfile to the first file. +return UNZ_OK if there is no problem +*/ +extern int ZEXPORT unzGoToFirstFile (unzFile file) +{ + int err=UNZ_OK; + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* +Set the current file of the zipfile to the next file. +return UNZ_OK if there is no problem +return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int ZEXPORT unzGoToNextFile (unzFile file) +{ + unz64_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* +Try locate the file szFileName in the zipfile. +For the iCaseSensitivity signification, see unzipStringFileNameCompare + +return value : +UNZ_OK if the file is found. It becomes the current file. +UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int ZEXPORT unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity) +{ + unz64_s* s; + int err; + + /* We remember the 'current' position in the file so that we can jump + * back there if we fail. + */ + unz_file_info64 cur_file_infoSaved; + unz_file_info64_internal cur_file_info_internalSaved; + ZPOS64_T num_fileSaved; + ZPOS64_T pos_in_central_dirSaved; + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + /* Save the current state */ + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + cur_file_infoSaved = s->cur_file_info; + cur_file_info_internalSaved = s->cur_file_info_internal; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + err = unzGetCurrentFileInfo64(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (err == UNZ_OK) + { + if (unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + } + + /* We failed, so restore the state of the 'current file' to where we + * were. + */ + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + s->cur_file_info = cur_file_infoSaved; + s->cur_file_info_internal = cur_file_info_internalSaved; + return err; +} + +/* +/////////////////////////////////////////// +// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) +// I need random access +// +// Further optimization could be realized by adding an ability +// to cache the directory in memory. The goal being a single +// comprehensive file read to put the file I need in a memory. +*/ + +/* +typedef struct unz_file_pos_s +{ +ZPOS64_T pos_in_zip_directory; // offset in file +ZPOS64_T num_of_file; // # of file +} unz_file_pos; +*/ + +extern int ZEXPORT unzGetFilePos64(unzFile file, unz64_file_pos* file_pos) +{ + unz64_s* s; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + file_pos->pos_in_zip_directory = s->pos_in_central_dir; + file_pos->num_of_file = s->num_file; + + return UNZ_OK; +} + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos) +{ + unz64_file_pos file_pos64; + int err = unzGetFilePos64(file,&file_pos64); + if (err==UNZ_OK) + { + file_pos->pos_in_zip_directory = (uLong)file_pos64.pos_in_zip_directory; + file_pos->num_of_file = (uLong)file_pos64.num_of_file; + } + return err; +} + +extern int ZEXPORT unzGoToFilePos64(unzFile file, const unz64_file_pos* file_pos) +{ + unz64_s* s; + int err; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + + /* jump to the right spot */ + s->pos_in_central_dir = file_pos->pos_in_zip_directory; + s->num_file = file_pos->num_of_file; + + /* set the current file */ + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + /* return results */ + s->current_file_ok = (err == UNZ_OK); + return err; +} + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos) +{ + unz64_file_pos file_pos64; + if (file_pos == NULL) + return UNZ_PARAMERROR; + + file_pos64.pos_in_zip_directory = file_pos->pos_in_zip_directory; + file_pos64.num_of_file = file_pos->num_of_file; + return unzGoToFilePos64(file,&file_pos64); +} + +/* +// Unzip Helper Functions - should be here? +/////////////////////////////////////////// +*/ + +/* +Read the local header of the current zipfile +Check the coherency of the local header and info in the end of central +directory about this file +store in *piSizeVar the size of extra info in local header +(filename and size of extra field data) +*/ +local int unz64local_CheckCurrentFileCoherencyHeader (unz64_s* s, uInt* piSizeVar, + ZPOS64_T * poffset_local_extrafield, + uInt * psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (ZSEEK64(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (err==UNZ_OK) + { + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + } + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; + /* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; + */ + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + /* #ifdef HAVE_BZIP2 */ + (s->cur_file_info.compression_method!=Z_BZIP2ED) && + /* #endif */ + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* +Open for reading data the current file in the zipfile. +If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile3 (unzFile file, int* method, + int* level, int raw, const char* password) +{ + int err=UNZ_OK; + uInt iSizeVar; + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + ZPOS64_T offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ +# ifndef NOUNCRYPT + char source[12]; +# else + if (password != NULL) + return UNZ_PARAMERROR; +# endif + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unz64local_CheckCurrentFileCoherencyHeader(s,&iSizeVar, &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip64_read_info_s*)ALLOC(sizeof(file_in_zip64_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + pfile_in_zip_read_info->raw=raw; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if (method!=NULL) + *method = (int)s->cur_file_info.compression_method; + + if (level!=NULL) + { + *level = 6; + switch (s->cur_file_info.flag & 0x06) + { + case 6 : *level = 1; break; + case 4 : *level = 2; break; + case 2 : *level = 9; break; + } + } + + if ((s->cur_file_info.compression_method!=0) && + /* #ifdef HAVE_BZIP2 */ + (s->cur_file_info.compression_method!=Z_BZIP2ED) && + /* #endif */ + (s->cur_file_info.compression_method!=Z_DEFLATED)) + + err=UNZ_BADZIPFILE; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->total_out_64=0; + pfile_in_zip_read_info->compression_method = s->cur_file_info.compression_method; + pfile_in_zip_read_info->filestream=s->filestream; + pfile_in_zip_read_info->z_filefunc=s->z_filefunc; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if ((s->cur_file_info.compression_method==Z_BZIP2ED) && (!raw)) + { +#ifdef HAVE_BZIP2 + pfile_in_zip_read_info->bstream.bzalloc = (void *(*) (void *, int, int))0; + pfile_in_zip_read_info->bstream.bzfree = (free_func)0; + pfile_in_zip_read_info->bstream.opaque = (voidpf)0; + pfile_in_zip_read_info->bstream.state = (voidpf)0; + + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = (voidpf)0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=BZ2_bzDecompressInit(&pfile_in_zip_read_info->bstream, 0, 0); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=Z_BZIP2ED; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } +#else + pfile_in_zip_read_info->raw=1; +#endif + } + else if ((s->cur_file_info.compression_method==Z_DEFLATED) && (!raw)) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = 0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=Z_DEFLATED; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + s->pfile_in_zip_read = pfile_in_zip_read_info; + s->encrypted = 0; + +# ifndef NOUNCRYPT + if (password != NULL) + { + int i; + s->pcrc_32_tab = get_crc_table(); + init_keys(password,s->keys,s->pcrc_32_tab); + if (ZSEEK64(s->z_filefunc, s->filestream, + s->pfile_in_zip_read->pos_in_zipfile + + s->pfile_in_zip_read->byte_before_the_zipfile, + SEEK_SET)!=0) + return UNZ_INTERNALERROR; + if(ZREAD64(s->z_filefunc, s->filestream,source, 12)<12) + return UNZ_INTERNALERROR; + + for (i = 0; i<12; i++) + zdecode(s->keys,s->pcrc_32_tab,source[i]); + + s->pfile_in_zip_read->pos_in_zipfile+=12; + s->encrypted=1; + } +# endif + + return UNZ_OK; +} + +extern int ZEXPORT unzOpenCurrentFile (unzFile file) +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); +} + +extern int ZEXPORT unzOpenCurrentFilePassword (unzFile file, const char* password) +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, password); +} + +extern int ZEXPORT unzOpenCurrentFile2 (unzFile file, int* method, int* level, int raw) +{ + return unzOpenCurrentFile3(file, method, level, raw, NULL); +} + +/** Addition for GDAL : START */ + +extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64( unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + s=(unz64_s*)file; + if (file==NULL) + return 0; //UNZ_PARAMERROR; + pfile_in_zip_read_info=s->pfile_in_zip_read; + if (pfile_in_zip_read_info==NULL) + return 0; //UNZ_PARAMERROR; + return pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile; +} + +/** Addition for GDAL : END */ + +/* +Read bytes from the current file. +buf contain buffer where data must be copied +len the size of buf. + +return the number of byte copied if somes bytes are copied +return 0 if the end of file was reached +return <0 with error code if there is an error +(UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int ZEXPORT unzReadCurrentFile (unzFile file, voidp buf, unsigned len) +{ + int err=UNZ_OK; + uInt iRead = 0; + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if ((pfile_in_zip_read_info->read_buffer == NULL)) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && + (!(pfile_in_zip_read_info->raw))) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + if ((len>pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in) && + (pfile_in_zip_read_info->raw)) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + if (ZREAD64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->read_buffer, + uReadThis)!=uReadThis) + return UNZ_ERRNO; + +# ifndef NOUNCRYPT + if(s->encrypted) + { + uInt i; + for(i=0;iread_buffer[i] = + zdecode(s->keys,s->pcrc_32_tab, + pfile_in_zip_read_info->read_buffer[i]); + } +# endif + + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) + { + uInt uDoCopy,i ; + + if ((pfile_in_zip_read_info->stream.avail_in == 0) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + return (iRead==0) ? UNZ_EOF : iRead; + + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;istream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uDoCopy; + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else if (pfile_in_zip_read_info->compression_method==Z_BZIP2ED) + { +#ifdef HAVE_BZIP2 + uLong uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + + pfile_in_zip_read_info->bstream.next_in = (char*)pfile_in_zip_read_info->stream.next_in; + pfile_in_zip_read_info->bstream.avail_in = pfile_in_zip_read_info->stream.avail_in; + pfile_in_zip_read_info->bstream.total_in_lo32 = pfile_in_zip_read_info->stream.total_in; + pfile_in_zip_read_info->bstream.total_in_hi32 = 0; + pfile_in_zip_read_info->bstream.next_out = (char*)pfile_in_zip_read_info->stream.next_out; + pfile_in_zip_read_info->bstream.avail_out = pfile_in_zip_read_info->stream.avail_out; + pfile_in_zip_read_info->bstream.total_out_lo32 = pfile_in_zip_read_info->stream.total_out; + pfile_in_zip_read_info->bstream.total_out_hi32 = 0; + + uTotalOutBefore = pfile_in_zip_read_info->bstream.total_out_lo32; + bufBefore = (const Bytef *)pfile_in_zip_read_info->bstream.next_out; + + err=BZ2_bzDecompress(&pfile_in_zip_read_info->bstream); + + uTotalOutAfter = pfile_in_zip_read_info->bstream.total_out_lo32; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,bufBefore, (uInt)(uOutThis)); + pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + pfile_in_zip_read_info->stream.next_in = (Bytef*)pfile_in_zip_read_info->bstream.next_in; + pfile_in_zip_read_info->stream.avail_in = pfile_in_zip_read_info->bstream.avail_in; + pfile_in_zip_read_info->stream.total_in = pfile_in_zip_read_info->bstream.total_in_lo32; + pfile_in_zip_read_info->stream.next_out = (Bytef*)pfile_in_zip_read_info->bstream.next_out; + pfile_in_zip_read_info->stream.avail_out = pfile_in_zip_read_info->bstream.avail_out; + pfile_in_zip_read_info->stream.total_out = pfile_in_zip_read_info->bstream.total_out_lo32; + + if (err==BZ_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=BZ_OK) + break; +#endif + } // end Z_BZIP2ED + else + { + ZPOS64_T uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + ZPOS64_T uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) + err = Z_DATA_ERROR; + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; +} + +/* +Give the current position in uncompressed data +*/ +extern z_off_t ZEXPORT unztell (unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + +extern ZPOS64_T ZEXPORT unztell64 (unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return (ZPOS64_T)-1; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return (ZPOS64_T)-1; + + return pfile_in_zip_read_info->total_out_64; +} + +/* +return 1 if the end of file was reached, 0 elsewhere +*/ +extern int ZEXPORT unzeof (unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + +/* +Read extra field from the current file (opened by unzOpenCurrentFile) +This is the local-header version of the extra field (sometimes, there is +more info in the local-header version than in the central-header) + +if buf==NULL, it return the size of the local extra field that can be read + +if buf!=NULL, len is the size of the buffer, the extra header is copied in +buf. +the return value is the number of bytes copied in buf, or (if <0) +the error code +*/ +extern int ZEXPORT unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + uInt read_now; + ZPOS64_T size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (ZREAD64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + buf,read_now)!=read_now) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* +Close the file in zip opened with unzipOpenCurrentFile +Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int ZEXPORT unzCloseCurrentFile (unzFile file) +{ + int err=UNZ_OK; + + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && + (!pfile_in_zip_read_info->raw)) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised == Z_DEFLATED) + inflateEnd(&pfile_in_zip_read_info->stream); +#ifdef HAVE_BZIP2 + else if (pfile_in_zip_read_info->stream_initialised == Z_BZIP2ED) + BZ2_bzDecompressEnd(&pfile_in_zip_read_info->bstream); +#endif + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; +} + +/* +Get the global comment string of the ZipFile, in the szComment buffer. +uSizeBuf is the size of the szComment buffer. +return the number of byte copied or an error code <0 +*/ +extern int ZEXPORT unzGetGlobalComment (unzFile file, char * szComment, uLong uSizeBuf) +{ + unz64_s* s; + uLong uReadThis ; + if (file==NULL) + return (int)UNZ_PARAMERROR; + s=(unz64_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (ZSEEK64(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (ZREAD64(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + +/* Additions by RX '2004 */ +extern ZPOS64_T ZEXPORT unzGetOffset64(unzFile file) +{ + unz64_s* s; + + if (file==NULL) + return 0; //UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return 0; + if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) + if (s->num_file==s->gi.number_entry) + return 0; + return s->pos_in_central_dir; +} + +extern uLong ZEXPORT unzGetOffset (unzFile file) +{ + ZPOS64_T offset64; + + if (file==NULL) + return 0; //UNZ_PARAMERROR; + offset64 = unzGetOffset64(file); + return (uLong)offset64; +} + +extern int ZEXPORT unzSetOffset64(unzFile file, ZPOS64_T pos) +{ + unz64_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + + s->pos_in_central_dir = pos; + s->num_file = s->gi.number_entry; /* hack */ + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos) +{ + return unzSetOffset64(file,pos); +} \ No newline at end of file diff --git a/LifeCore/minizip/unzip.h b/LifeCore/minizip/unzip.h new file mode 100644 index 0000000..9362912 --- /dev/null +++ b/LifeCore/minizip/unzip.h @@ -0,0 +1,426 @@ +/* unzip.h -- IO for uncompress .zip files using zlib +Version 1.1, February 14h, 2010 +part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + +Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + +Modifications of Unzip for Zip64 +Copyright (C) 2007-2008 Even Rouault + +Modifications for Zip64 support on both zip and unzip +Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + +For more info read MiniZip_info.txt + +--------------------------------------------------------------------------------- + +Condition of use and distribution are the same than zlib : + +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. + +--------------------------------------------------------------------------------- + +Changes + +See header of unzip64.c + +*/ + +#ifndef _unz64_H +#define _unz64_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#ifdef HAVE_BZIP2 +#include "bzlib.h" +#endif + +#define Z_BZIP2ED 12 + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) + /* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ + typedef struct TagunzFile__ { int unused; } unzFile__; + typedef unzFile__ *unzFile; +#else + typedef voidp unzFile; +#endif + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + + /* tm_unz contain date/time info */ + typedef struct tm_unz_s + { + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ + } tm_unz; + + /* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ + typedef struct unz_global_info64_s + { + ZPOS64_T number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ + } unz_global_info64; + + typedef struct unz_global_info_s + { + uLong number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ + } unz_global_info; + + /* unz_file_info contain information about a file in the zipfile */ + typedef struct unz_file_info64_s + { + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + ZPOS64_T compressed_size; /* compressed size 8 bytes */ + ZPOS64_T uncompressed_size; /* uncompressed size 8 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; + } unz_file_info64; + + typedef struct unz_file_info_s + { + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong compressed_size; /* compressed size 4 bytes */ + uLong uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; + } unz_file_info; + + extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); + /* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + */ + + extern unzFile ZEXPORT unzOpen OF((const char *path)); + extern unzFile ZEXPORT unzOpen64 OF((const void *path)); + /* + Open a Zip file. path contain the full pathname (by example, + on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer + "zlib/zlib113.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. + the "64" function take a const void* pointer, because the path is just the + value passed to the open64_file_func callback. + Under Windows, if UNICODE is defined, using fill_fopen64_filefunc, the path + is a pointer to a wide unicode string (LPCTSTR is LPCWSTR), so const char* + does not describe the reality + */ + + extern unzFile ZEXPORT unzOpen2 OF((const char *path, + zlib_filefunc_def* pzlib_filefunc_def)); + /* + Open a Zip file, like unzOpen, but provide a set of file low level API + for read/write the zip file (see ioapi.h) + */ + + extern unzFile ZEXPORT unzOpen2_64 OF((const void *path, + zlib_filefunc64_def* pzlib_filefunc_def)); + /* + Open a Zip file, like unz64Open, but provide a set of file low level API + for read/write the zip file (see ioapi.h) + */ + + extern int ZEXPORT unzClose OF((unzFile file)); + /* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ + + extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); + + extern int ZEXPORT unzGetGlobalInfo64 OF((unzFile file, + unz_global_info64 *pglobal_info)); + /* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); + /* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 + */ + + /***************************************************************************/ + /* Unzip package allow you browse the directory of the zipfile */ + + extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); + /* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem + */ + + extern int ZEXPORT unzGoToNextFile OF((unzFile file)); + /* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. + */ + + extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); + /* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found + */ + + /* ****************************************** */ + /* Ryan supplied functions */ + /* unz_file_info contain information about a file in the zipfile */ + typedef struct unz_file_pos_s + { + uLong pos_in_zip_directory; /* offset in zip file directory */ + uLong num_of_file; /* # of file */ + } unz_file_pos; + + extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos); + + extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos); + + typedef struct unz64_file_pos_s + { + ZPOS64_T pos_in_zip_directory; /* offset in zip file directory */ + ZPOS64_T num_of_file; /* # of file */ + } unz64_file_pos; + + extern int ZEXPORT unzGetFilePos64( + unzFile file, + unz64_file_pos* file_pos); + + extern int ZEXPORT unzGoToFilePos64( + unzFile file, + const unz64_file_pos* file_pos); + + /* ****************************************** */ + + extern int ZEXPORT unzGetCurrentFileInfo64 OF((unzFile file, + unz_file_info64 *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + + extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + /* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) + */ + + /** Addition for GDAL : START */ + + extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64 OF((unzFile file)); + + /** Addition for GDAL : END */ + + /***************************************************************************/ + /* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + + extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); + /* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. + */ + + extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, + const char* password)); + /* + Open for reading data the current file in the zipfile. + password is a crypting password + If there is no error, the return value is UNZ_OK. + */ + + extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, + int* method, + int* level, + int raw)); + /* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL + */ + + extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, + int* method, + int* level, + int raw, + const char* password)); + /* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL + */ + + extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); + /* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good + */ + + extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); + /* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) + */ + + extern z_off_t ZEXPORT unztell OF((unzFile file)); + + extern ZPOS64_T ZEXPORT unztell64 OF((unzFile file)); + /* + Give the current position in uncompressed data + */ + + extern int ZEXPORT unzeof OF((unzFile file)); + /* + return 1 if the end of file was reached, 0 elsewhere + */ + + extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); + /* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code + */ + + /***************************************************************************/ + + /* Get the current file offset */ + extern ZPOS64_T ZEXPORT unzGetOffset64 (unzFile file); + extern uLong ZEXPORT unzGetOffset (unzFile file); + + /* Set the current file offset */ + extern int ZEXPORT unzSetOffset64 (unzFile file, ZPOS64_T pos); + extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); + +#ifdef __cplusplus +} +#endif + +#endif /* _unz64_H */ \ No newline at end of file diff --git a/LifeCore/minizip/zip.c b/LifeCore/minizip/zip.c new file mode 100644 index 0000000..97227d2 --- /dev/null +++ b/LifeCore/minizip/zip.c @@ -0,0 +1,1974 @@ +/* zip.c -- IO on .zip files using zlib +Version 1.1, February 14h, 2010 +part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + +Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + +Modifications for Zip64 support +Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + +For more info read MiniZip_info.txt + +Changes +Oct-2009 - Mathias Svensson - Remove old C style function prototypes +Oct-2009 - Mathias Svensson - Added Zip64 Support when creating new file archives +Oct-2009 - Mathias Svensson - Did some code cleanup and refactoring to get better overview of some functions. +Oct-2009 - Mathias Svensson - Added zipRemoveExtraInfoBlock to strip extra field data from its ZIP64 data +It is used when recreting zip archive with RAW when deleting items from a zip. +ZIP64 data is automaticly added to items that needs it, and existing ZIP64 data need to be removed. +Oct-2009 - Mathias Svensson - Added support for BZIP2 as compression mode (bzip2 lib is required) +Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer + +*/ + +#include +#include +#include +#include +#include "zlib.h" +#include "zip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H +extern int errno; +#else +# include +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +#ifndef VERSIONMADEBY +# define VERSIONMADEBY (0x0) /* platform depedent */ +#endif + +#ifndef Z_BUFSIZE +#define Z_BUFSIZE (64*1024) //(16384) +#endif + +#ifndef Z_MAXFILENAMEINZIP +#define Z_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +/* +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) +*/ + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + +// NOT sure that this work on ALL platform +#define MAKEULONG64(a, b) ((ZPOS64_T)(((unsigned long)(a)) | ((ZPOS64_T)((unsigned long)(b))) << 32)) + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +#ifndef DEF_MEM_LEVEL +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +#endif +const char zip_copyright[] =" zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + +#define SIZEDATA_INDATABLOCK (4096-(4*4)) + +#define LOCALHEADERMAGIC (0x04034b50) +#define CENTRALHEADERMAGIC (0x02014b50) +#define ENDHEADERMAGIC (0x06054b50) +#define ZIP64ENDHEADERMAGIC (0x6064b50) +#define ZIP64ENDLOCHEADERMAGIC (0x7064b50) + +#define FLAG_LOCALHEADER_OFFSET (0x06) +#define CRC_LOCALHEADER_OFFSET (0x0e) + +#define SIZECENTRALHEADER (0x2e) /* 46 */ + +typedef struct linkedlist_datablock_internal_s +{ + struct linkedlist_datablock_internal_s* next_datablock; + uLong avail_in_this_block; + uLong filled_in_this_block; + uLong unused; /* for future use and alignement */ + unsigned char data[SIZEDATA_INDATABLOCK]; +} linkedlist_datablock_internal; + +typedef struct linkedlist_data_s +{ + linkedlist_datablock_internal* first_block; + linkedlist_datablock_internal* last_block; +} linkedlist_data; + +typedef struct +{ + z_stream stream; /* zLib stream structure for inflate */ +#ifdef HAVE_BZIP2 + bz_stream bstream; /* bzLib stream structure for bziped */ +#endif + + int stream_initialised; /* 1 is stream is initialised */ + uInt pos_in_buffered_data; /* last written byte in buffered_data */ + + ZPOS64_T pos_local_header; /* offset of the local header of the file + currenty writing */ + char* central_header; /* central header data for the current file */ + uLong size_centralExtra; + uLong size_centralheader; /* size of the central header for cur file */ + uLong size_centralExtraFree; /* Extra bytes allocated to the centralheader but that are not used */ + uLong flag; /* flag of the file currently writing */ + + int method; /* compression method of file currenty wr.*/ + int raw; /* 1 for directly writing raw data */ + Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/ + uLong dosDate; + uLong crc32; + int encrypt; + int zip64; /* Add ZIP64 extened information in the extra field */ + ZPOS64_T pos_zip64extrainfo; + ZPOS64_T totalCompressedData; + ZPOS64_T totalUncompressedData; +#ifndef NOCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const unsigned long* pcrc_32_tab; + int crypt_header_size; +#endif +} curfile64_info; + +typedef struct +{ + zlib_filefunc64_32_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + linkedlist_data central_dir;/* datablock with central dir in construction*/ + int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/ + curfile64_info ci; /* info on the file curretly writing */ + + ZPOS64_T begin_pos; /* position of the beginning of the zipfile */ + ZPOS64_T add_position_when_writting_offset; + ZPOS64_T number_entry; + +#ifndef NO_ADDFILEINEXISTINGZIP + char *globalcomment; +#endif +} zip64_internal; + +#ifndef NOCRYPT +#define INCLUDECRYPTINGCODE_IFCRYPTALLOWED +#include "crypt.h" +#endif + +local linkedlist_datablock_internal* allocate_new_datablock() +{ + linkedlist_datablock_internal* ldi; + ldi = (linkedlist_datablock_internal*) + ALLOC(sizeof(linkedlist_datablock_internal)); + if (ldi!=NULL) + { + ldi->next_datablock = NULL ; + ldi->filled_in_this_block = 0 ; + ldi->avail_in_this_block = SIZEDATA_INDATABLOCK ; + } + return ldi; +} + +local void free_datablock(linkedlist_datablock_internal* ldi) +{ + while (ldi!=NULL) + { + linkedlist_datablock_internal* ldinext = ldi->next_datablock; + TRYFREE(ldi); + ldi = ldinext; + } +} + +local void init_linkedlist(linkedlist_data* ll) +{ + ll->first_block = ll->last_block = NULL; +} + +local void free_linkedlist(linkedlist_data* ll) +{ + free_datablock(ll->first_block); + ll->first_block = ll->last_block = NULL; +} + +local int add_data_in_datablock(linkedlist_data* ll, const void* buf, uLong len) +{ + linkedlist_datablock_internal* ldi; + const unsigned char* from_copy; + + if (ll==NULL) + return ZIP_INTERNALERROR; + + if (ll->last_block == NULL) + { + ll->first_block = ll->last_block = allocate_new_datablock(); + if (ll->first_block == NULL) + return ZIP_INTERNALERROR; + } + + ldi = ll->last_block; + from_copy = (unsigned char*)buf; + + while (len>0) + { + uInt copy_this; + uInt i; + unsigned char* to_copy; + + if (ldi->avail_in_this_block==0) + { + ldi->next_datablock = allocate_new_datablock(); + if (ldi->next_datablock == NULL) + return ZIP_INTERNALERROR; + ldi = ldi->next_datablock ; + ll->last_block = ldi; + } + + if (ldi->avail_in_this_block < len) + copy_this = (uInt)ldi->avail_in_this_block; + else + copy_this = (uInt)len; + + to_copy = &(ldi->data[ldi->filled_in_this_block]); + + for (i=0;ifilled_in_this_block += copy_this; + ldi->avail_in_this_block -= copy_this; + from_copy += copy_this ; + len -= copy_this; + } + return ZIP_OK; +} + +/****************************************************************************/ + +#ifndef NO_ADDFILEINEXISTINGZIP +/* =========================================================================== +Inputs a long in LSB order to the given file +nbByte == 1, 2 ,4 or 8 (byte, short or long, ZPOS64_T) +*/ + +local int zip64local_putValue OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte)); +local int zip64local_putValue (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte) +{ + unsigned char buf[8]; + int n; + for (n = 0; n < nbByte; n++) + { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + if (x != 0) + { /* data overflow - hack for ZIP64 (X Roche) */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } + + if (ZWRITE64(*pzlib_filefunc_def,filestream,buf,nbByte)!=(uLong)nbByte) + return ZIP_ERRNO; + else + return ZIP_OK; +} + +local void zip64local_putValue_inmemory OF((void* dest, ZPOS64_T x, int nbByte)); +local void zip64local_putValue_inmemory (void* dest, ZPOS64_T x, int nbByte) +{ + unsigned char* buf=(unsigned char*)dest; + int n; + for (n = 0; n < nbByte; n++) { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + + if (x != 0) + { /* data overflow - hack for ZIP64 */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } +} + +/****************************************************************************/ + +local uLong zip64local_TmzDateToDosDate(const tm_zip* ptm) +{ + uLong year = (uLong)ptm->tm_year; + if (year>=1980) + year-=1980; + else if (year>=80) + year-=80; + return + (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) | + ((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour)); +} + +/****************************************************************************/ + +local int zip64local_getByte OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi)); + +local int zip64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def,voidpf filestream,int* pi) +{ + unsigned char c; + int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return ZIP_OK; + } + else + { + if (ZERROR64(*pzlib_filefunc_def,filestream)) + return ZIP_ERRNO; + else + return ZIP_EOF; + } +} + +/* =========================================================================== +Reads a long in LSB order from the given gz_stream. Sets +*/ +local int zip64local_getShort OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); + +local int zip64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) +{ + uLong x ; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int zip64local_getLong OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); + +local int zip64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) +{ + uLong x ; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<16; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int zip64local_getLong64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX)); + +local int zip64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX) +{ + ZPOS64_T x; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (ZPOS64_T)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<8; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<16; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<24; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<32; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<40; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<48; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<56; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + + return err; +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif +/* +Locate the Central directory of a zipfile (at the end, just before +the global comment) +*/ +local ZPOS64_T zip64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); + +local ZPOS64_T zip64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* +Locate the End of Zip64 Central directory locator and from there find the CD of a zipfile (at the end, just before +the global comment) +*/ +local ZPOS64_T zip64local_SearchCentralDir64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); + +local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + uLong uL; + ZPOS64_T relativeOffset; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + { + // Signature "0x07064b50" Zip64 end of central directory locater + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) + { + uPosFound = uReadPos+i; + break; + } + } + + if (uPosFound!=0) + break; + } + + TRYFREE(buf); + if (uPosFound == 0) + return 0; + + /* Zip64 end of central directory locator */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature, already checked */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + + /* number of the disk with the start of the zip64 end of central directory */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + if (uL != 0) + return 0; + + /* relative offset of the zip64 end of central directory record */ + if (zip64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=ZIP_OK) + return 0; + + /* total number of disks */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + if (uL != 1) + return 0; + + /* Goto Zip64 end of central directory record */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + + if (uL != 0x06064b50) // signature of 'Zip64 end of central directory' + return 0; + + return relativeOffset; +} + +int LoadCentralDirectoryRecord(zip64_internal* pziinit) +{ + int err=ZIP_OK; + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + + ZPOS64_T size_central_dir; /* size of the central directory */ + ZPOS64_T offset_central_dir; /* offset of start of central directory */ + ZPOS64_T central_pos; + uLong uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + ZPOS64_T number_entry; + ZPOS64_T number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + uLong VersionMadeBy; + uLong VersionNeeded; + uLong size_comment; + + int hasZIP64Record = 0; + + // check first if we find a ZIP64 record + central_pos = zip64local_SearchCentralDir64(&pziinit->z_filefunc,pziinit->filestream); + if(central_pos > 0) + { + hasZIP64Record = 1; + } + else if(central_pos == 0) + { + central_pos = zip64local_SearchCentralDir(&pziinit->z_filefunc,pziinit->filestream); + } + + /* disable to allow appending to empty ZIP archive + if (central_pos==0) + err=ZIP_ERRNO; + */ + + if(hasZIP64Record) + { + ZPOS64_T sizeEndOfCentralDirectory; + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + /* the signature, already checked */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK) + err=ZIP_ERRNO; + + /* size of zip64 end of central directory record */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &sizeEndOfCentralDirectory)!=ZIP_OK) + err=ZIP_ERRNO; + + /* version made by */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionMadeBy)!=ZIP_OK) + err=ZIP_ERRNO; + + /* version needed to extract */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionNeeded)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of this disk */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of the disk with the start of the central directory */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central directory on this disk */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &number_entry)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central directory */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&number_entry_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) + err=ZIP_BADZIPFILE; + + /* size of the central directory */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&size_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&offset_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + // TODO.. + // read the comment from the standard central header. + size_comment = 0; + } + else + { + // Read End of central Directory info + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=ZIP_ERRNO; + + /* the signature, already checked */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of this disk */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of the disk with the start of the central directory */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central dir on this disk */ + number_entry = 0; + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + number_entry = uL; + + /* total number of entries in the central dir */ + number_entry_CD = 0; + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + number_entry_CD = uL; + + if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) + err=ZIP_BADZIPFILE; + + /* size of the central directory */ + size_central_dir = 0; + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + size_central_dir = uL; + + /* offset of start of central directory with respect to the starting disk number */ + offset_central_dir = 0; + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + offset_central_dir = uL; + + /* zipfile global comment length */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &size_comment)!=ZIP_OK) + err=ZIP_ERRNO; + } + + if ((central_posz_filefunc, pziinit->filestream); + return ZIP_ERRNO; + } + + if (size_comment>0) + { + pziinit->globalcomment = (char*)ALLOC(size_comment+1); + if (pziinit->globalcomment) + { + size_comment = ZREAD64(pziinit->z_filefunc, pziinit->filestream, pziinit->globalcomment,size_comment); + pziinit->globalcomment[size_comment]=0; + } + } + + byte_before_the_zipfile = central_pos - (offset_central_dir+size_central_dir); + pziinit->add_position_when_writting_offset = byte_before_the_zipfile; + + { + ZPOS64_T size_central_dir_to_read = size_central_dir; + size_t buf_size = SIZEDATA_INDATABLOCK; + void* buf_read = (void*)ALLOC(buf_size); + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir + byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + while ((size_central_dir_to_read>0) && (err==ZIP_OK)) + { + ZPOS64_T read_this = SIZEDATA_INDATABLOCK; + if (read_this > size_central_dir_to_read) + read_this = size_central_dir_to_read; + + if (ZREAD64(pziinit->z_filefunc, pziinit->filestream,buf_read,(uLong)read_this) != read_this) + err=ZIP_ERRNO; + + if (err==ZIP_OK) + err = add_data_in_datablock(&pziinit->central_dir,buf_read, (uLong)read_this); + + size_central_dir_to_read-=read_this; + } + TRYFREE(buf_read); + } + pziinit->begin_pos = byte_before_the_zipfile; + pziinit->number_entry = number_entry_CD; + + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir+byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + return err; +} + +#endif /* !NO_ADDFILEINEXISTINGZIP*/ + +/************************************************************/ +extern zipFile ZEXPORT zipOpen3 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_32_def* pzlib_filefunc64_32_def) +{ + zip64_internal ziinit; + zip64_internal* zi; + int err=ZIP_OK; + + ziinit.z_filefunc.zseek32_file = NULL; + ziinit.z_filefunc.ztell32_file = NULL; + if (pzlib_filefunc64_32_def==NULL) + fill_fopen64_filefunc(&ziinit.z_filefunc.zfile_func64); + else + ziinit.z_filefunc = *pzlib_filefunc64_32_def; + + ziinit.filestream = ZOPEN64(ziinit.z_filefunc, + pathname, + (append == APPEND_STATUS_CREATE) ? + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE) : + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING)); + + if (ziinit.filestream == NULL) + return NULL; + + if (append == APPEND_STATUS_CREATEAFTER) + ZSEEK64(ziinit.z_filefunc,ziinit.filestream,0,SEEK_END); + + ziinit.begin_pos = ZTELL64(ziinit.z_filefunc,ziinit.filestream); + ziinit.in_opened_file_inzip = 0; + ziinit.ci.stream_initialised = 0; + ziinit.number_entry = 0; + ziinit.add_position_when_writting_offset = 0; + init_linkedlist(&(ziinit.central_dir)); + + zi = (zip64_internal*)ALLOC(sizeof(zip64_internal)); + if (zi==NULL) + { + ZCLOSE64(ziinit.z_filefunc,ziinit.filestream); + return NULL; + } + + /* now we add file in a zipfile */ +# ifndef NO_ADDFILEINEXISTINGZIP + ziinit.globalcomment = NULL; + if (append == APPEND_STATUS_ADDINZIP) + { + // Read and Cache Central Directory Records + err = LoadCentralDirectoryRecord(&ziinit); + } + + if (globalcomment) + { + *globalcomment = ziinit.globalcomment; + } +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + + if (err != ZIP_OK) + { +# ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(ziinit.globalcomment); +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + TRYFREE(zi); + return NULL; + } + else + { + *zi = ziinit; + return (zipFile)zi; + } +} + +extern zipFile ZEXPORT zipOpen2 (const char *pathname, int append, zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc32_def) +{ + if (pzlib_filefunc32_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; + fill_zlib_filefunc64_32_def_from_filefunc32(&zlib_filefunc64_32_def_fill,pzlib_filefunc32_def); + return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill); + } + else + return zipOpen3(pathname, append, globalcomment, NULL); +} + +extern zipFile ZEXPORT zipOpen2_64 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_def* pzlib_filefunc_def) +{ + if (pzlib_filefunc_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; + zlib_filefunc64_32_def_fill.zfile_func64 = *pzlib_filefunc_def; + zlib_filefunc64_32_def_fill.ztell32_file = NULL; + zlib_filefunc64_32_def_fill.zseek32_file = NULL; + return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill); + } + else + return zipOpen3(pathname, append, globalcomment, NULL); +} + +extern zipFile ZEXPORT zipOpen (const char* pathname, int append) +{ + return zipOpen3((const void*)pathname,append,NULL,NULL); +} + +extern zipFile ZEXPORT zipOpen64 (const void* pathname, int append) +{ + return zipOpen3(pathname,append,NULL,NULL); +} + +int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local) +{ + /* write the local header */ + int err; + uInt size_filename = (uInt)strlen(filename); + uInt size_extrafield = size_extrafield_local; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)LOCALHEADERMAGIC, 4); + + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2);/* version needed to extract */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)20,2);/* version needed to extract */ + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.flag,2); + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.method,2); + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.dosDate,4); + + // CRC / Compressed size / Uncompressed size will be filled in later and rewritten later + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* crc 32, unknown */ + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* compressed size, unknown */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* compressed size, unknown */ + } + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* uncompressed size, unknown */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* uncompressed size, unknown */ + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_filename,2); + + if(zi->ci.zip64) + { + size_extrafield += 20; + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_extrafield,2); + + if ((err==ZIP_OK) && (size_filename > 0)) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream,filename,size_filename)!=size_filename) + err = ZIP_ERRNO; + } + + if ((err==ZIP_OK) && (size_extrafield_local > 0)) + { + if (ZWRITE64(zi->z_filefunc, zi->filestream, extrafield_local, size_extrafield_local) != size_extrafield_local) + err = ZIP_ERRNO; + } + + if ((err==ZIP_OK) && (zi->ci.zip64)) + { + // write the Zip64 extended info + short HeaderID = 1; + short DataSize = 16; + ZPOS64_T CompressedSize = 0; + ZPOS64_T UncompressedSize = 0; + + // Remember position of Zip64 extended info for the local file header. (needed when we update size after done with file) + zi->ci.pos_zip64extrainfo = ZTELL64(zi->z_filefunc,zi->filestream); + + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)HeaderID,2); + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)DataSize,2); + + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)UncompressedSize,8); + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)CompressedSize,8); + } + + return err; +} + +/* +NOTE. +When writing RAW the ZIP64 extended information in extrafield_local and extrafield_global needs to be stripped +before calling this function it can be done with zipRemoveExtraInfoBlock + +It is not done here because then we need to realloc a new buffer since parameters are 'const' and I want to minimize +unnecessary allocations. +*/ +extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, + uLong versionMadeBy, uLong flagBase, int zip64) +{ + zip64_internal* zi; + uInt size_filename; + uInt size_comment; + uInt i; + int err = ZIP_OK; + +# ifdef NOCRYPT + if (password != NULL) + return ZIP_PARAMERROR; +# endif + + if (file == NULL) + return ZIP_PARAMERROR; + +#ifdef HAVE_BZIP2 + if ((method!=0) && (method!=Z_DEFLATED) && (method!=Z_BZIP2ED)) + return ZIP_PARAMERROR; +#else + if ((method!=0) && (method!=Z_DEFLATED)) + return ZIP_PARAMERROR; +#endif + + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + if (err != ZIP_OK) + return err; + } + + if (filename==NULL) + filename="-"; + + if (comment==NULL) + size_comment = 0; + else + size_comment = (uInt)strlen(comment); + + size_filename = (uInt)strlen(filename); + + if (zipfi == NULL) + zi->ci.dosDate = 0; + else + { + if (zipfi->dosDate != 0) + zi->ci.dosDate = zipfi->dosDate; + else + zi->ci.dosDate = zip64local_TmzDateToDosDate(&zipfi->tmz_date); + } + + zi->ci.flag = flagBase; + if ((level==8) || (level==9)) + zi->ci.flag |= 2; + if ((level==2)) + zi->ci.flag |= 4; + if ((level==1)) + zi->ci.flag |= 6; + if (password != NULL) + zi->ci.flag |= 1; + + zi->ci.crc32 = 0; + zi->ci.method = method; + zi->ci.encrypt = 0; + zi->ci.stream_initialised = 0; + zi->ci.pos_in_buffered_data = 0; + zi->ci.raw = raw; + zi->ci.pos_local_header = ZTELL64(zi->z_filefunc,zi->filestream); + + zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename + size_extrafield_global + size_comment; + zi->ci.size_centralExtraFree = 32; // Extra space we have reserved in case we need to add ZIP64 extra info data + + zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader + zi->ci.size_centralExtraFree); + + zi->ci.size_centralExtra = size_extrafield_global; + zip64local_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4); + /* version info */ + zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)versionMadeBy,2); + zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)20,2); + zip64local_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2); + zip64local_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2); + zip64local_putValue_inmemory(zi->ci.central_header+12,(uLong)zi->ci.dosDate,4); + zip64local_putValue_inmemory(zi->ci.central_header+16,(uLong)0,4); /*crc*/ + zip64local_putValue_inmemory(zi->ci.central_header+20,(uLong)0,4); /*compr size*/ + zip64local_putValue_inmemory(zi->ci.central_header+24,(uLong)0,4); /*uncompr size*/ + zip64local_putValue_inmemory(zi->ci.central_header+28,(uLong)size_filename,2); + zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)size_extrafield_global,2); + zip64local_putValue_inmemory(zi->ci.central_header+32,(uLong)size_comment,2); + zip64local_putValue_inmemory(zi->ci.central_header+34,(uLong)0,2); /*disk nm start*/ + + if (zipfi==NULL) + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)0,2); + else + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)zipfi->internal_fa,2); + + if (zipfi==NULL) + zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)0,4); + else + zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)zipfi->external_fa,4); + + if(zi->ci.pos_local_header >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)0xffffffff,4); + else + zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header - zi->add_position_when_writting_offset,4); + + for (i=0;ici.central_header+SIZECENTRALHEADER+i) = *(filename+i); + + for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+i) = + *(((const char*)extrafield_global)+i); + + for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+ + size_extrafield_global+i) = *(comment+i); + if (zi->ci.central_header == NULL) + return ZIP_INTERNALERROR; + + zi->ci.zip64 = zip64; + zi->ci.totalCompressedData = 0; + zi->ci.totalUncompressedData = 0; + zi->ci.pos_zip64extrainfo = 0; + + err = Write_LocalFileHeader(zi, filename, size_extrafield_local, extrafield_local); + +#ifdef HAVE_BZIP2 + zi->ci.bstream.avail_in = (uInt)0; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + zi->ci.bstream.total_in_hi32 = 0; + zi->ci.bstream.total_in_lo32 = 0; + zi->ci.bstream.total_out_hi32 = 0; + zi->ci.bstream.total_out_lo32 = 0; +#endif + + zi->ci.stream.avail_in = (uInt)0; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + zi->ci.stream.total_in = 0; + zi->ci.stream.total_out = 0; + zi->ci.stream.data_type = Z_BINARY; + +#ifdef HAVE_BZIP2 + if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED || zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) +#else + if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) +#endif + { + if(zi->ci.method == Z_DEFLATED) + { + zi->ci.stream.zalloc = (alloc_func)0; + zi->ci.stream.zfree = (free_func)0; + zi->ci.stream.opaque = (voidpf)0; + + if (windowBits>0) + windowBits = -windowBits; + + err = deflateInit2(&zi->ci.stream, level, Z_DEFLATED, windowBits, memLevel, strategy); + + if (err==Z_OK) + zi->ci.stream_initialised = Z_DEFLATED; + } + else if(zi->ci.method == Z_BZIP2ED) + { +#ifdef HAVE_BZIP2 + // Init BZip stuff here + zi->ci.bstream.bzalloc = 0; + zi->ci.bstream.bzfree = 0; + zi->ci.bstream.opaque = (voidpf)0; + + err = BZ2_bzCompressInit(&zi->ci.bstream, level, 0,35); + if(err == BZ_OK) + zi->ci.stream_initialised = Z_BZIP2ED; +#endif + } + } + +# ifndef NOCRYPT + zi->ci.crypt_header_size = 0; + if ((err==Z_OK) && (password != NULL)) + { + unsigned char bufHead[RAND_HEAD_LEN]; + unsigned int sizeHead; + zi->ci.encrypt = 1; + zi->ci.pcrc_32_tab = get_crc_table(); + /*init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab);*/ + + sizeHead=crypthead(password,bufHead,RAND_HEAD_LEN,zi->ci.keys,zi->ci.pcrc_32_tab,crcForCrypting); + zi->ci.crypt_header_size = sizeHead; + + if (ZWRITE64(zi->z_filefunc,zi->filestream,bufHead,sizeHead) != sizeHead) + err = ZIP_ERRNO; + } +# endif + + if (err==Z_OK) + zi->in_opened_file_inzip = 1; + return err; +} + +extern int ZEXPORT zipOpenNewFileInZip4 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, + uLong versionMadeBy, uLong flagBase) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, versionMadeBy, flagBase, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip3 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, VERSIONMADEBY, 0, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip2(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip2_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void*extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void*extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, 0); +} + +local int zip64FlushWriteBuffer(zip64_internal* zi) +{ + int err=ZIP_OK; + + if (zi->ci.encrypt != 0) + { +#ifndef NOCRYPT + uInt i; + int t; + for (i=0;ici.pos_in_buffered_data;i++) + zi->ci.buffered_data[i] = zencode(zi->ci.keys, zi->ci.pcrc_32_tab, zi->ci.buffered_data[i],t); +#endif + } + + if (ZWRITE64(zi->z_filefunc,zi->filestream,zi->ci.buffered_data,zi->ci.pos_in_buffered_data) != zi->ci.pos_in_buffered_data) + err = ZIP_ERRNO; + + zi->ci.totalCompressedData += zi->ci.pos_in_buffered_data; + +#ifdef HAVE_BZIP2 + if(zi->ci.method == Z_BZIP2ED) + { + zi->ci.totalUncompressedData += zi->ci.bstream.total_in_lo32; + zi->ci.bstream.total_in_lo32 = 0; + zi->ci.bstream.total_in_hi32 = 0; + } + else +#endif + { + zi->ci.totalUncompressedData += zi->ci.stream.total_in; + zi->ci.stream.total_in = 0; + } + + zi->ci.pos_in_buffered_data = 0; + + return err; +} + +extern int ZEXPORT zipWriteInFileInZip (zipFile file,const void* buf,unsigned int len) +{ + zip64_internal* zi; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + + zi->ci.crc32 = crc32(zi->ci.crc32,buf,(uInt)len); + +#ifdef HAVE_BZIP2 + if(zi->ci.method == Z_BZIP2ED && (!zi->ci.raw)) + { + zi->ci.bstream.next_in = (void*)buf; + zi->ci.bstream.avail_in = len; + err = BZ_RUN_OK; + + while ((err==BZ_RUN_OK) && (zi->ci.bstream.avail_in>0)) + { + if (zi->ci.bstream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + } + + if(err != BZ_RUN_OK) + break; + + if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { + uLong uTotalOutBefore_lo = zi->ci.bstream.total_out_lo32; + // uLong uTotalOutBefore_hi = zi->ci.bstream.total_out_hi32; + err=BZ2_bzCompress(&zi->ci.bstream, BZ_RUN); + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore_lo) ; + } + } + + if(err == BZ_RUN_OK) + err = ZIP_OK; + } + else +#endif + { + zi->ci.stream.next_in = (Bytef*)buf; + zi->ci.stream.avail_in = len; + + while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0)) + { + if (zi->ci.stream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + + if(err != ZIP_OK) + break; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + uLong uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_NO_FLUSH); + if(uTotalOutBefore > zi->ci.stream.total_out) + { + int bBreak = 0; + bBreak++; + } + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + } + else + { + uInt copy_this,i; + if (zi->ci.stream.avail_in < zi->ci.stream.avail_out) + copy_this = zi->ci.stream.avail_in; + else + copy_this = zi->ci.stream.avail_out; + + for (i = 0; i < copy_this; i++) + *(((char*)zi->ci.stream.next_out)+i) = + *(((const char*)zi->ci.stream.next_in)+i); + { + zi->ci.stream.avail_in -= copy_this; + zi->ci.stream.avail_out-= copy_this; + zi->ci.stream.next_in+= copy_this; + zi->ci.stream.next_out+= copy_this; + zi->ci.stream.total_in+= copy_this; + zi->ci.stream.total_out+= copy_this; + zi->ci.pos_in_buffered_data += copy_this; + } + } + }// while(...) + } + + return err; +} + +extern int ZEXPORT zipCloseFileInZipRaw (zipFile file, uLong uncompressed_size, uLong crc32) +{ + return zipCloseFileInZipRaw64 (file, uncompressed_size, crc32); +} + +extern int ZEXPORT zipCloseFileInZipRaw64 (zipFile file, ZPOS64_T uncompressed_size, uLong crc32) +{ + zip64_internal* zi; + ZPOS64_T compressed_size; + uLong invalidValue = 0xffffffff; + short datasize = 0; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + zi->ci.stream.avail_in = 0; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + while (err==ZIP_OK) + { + uLong uTotalOutBefore; + if (zi->ci.stream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_FINISH); + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + } + } + else if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { +#ifdef HAVE_BZIP2 + err = BZ_FINISH_OK; + while (err==BZ_FINISH_OK) + { + uLong uTotalOutBefore; + if (zi->ci.bstream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + } + uTotalOutBefore = zi->ci.bstream.total_out_lo32; + err=BZ2_bzCompress(&zi->ci.bstream, BZ_FINISH); + if(err == BZ_STREAM_END) + err = Z_STREAM_END; + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore); + } + + if(err == BZ_FINISH_OK) + err = ZIP_OK; +#endif + } + + if (err==Z_STREAM_END) + err=ZIP_OK; /* this is normal */ + + if ((zi->ci.pos_in_buffered_data>0) && (err==ZIP_OK)) + { + if (zip64FlushWriteBuffer(zi)==ZIP_ERRNO) + err = ZIP_ERRNO; + } + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + int tmp_err = deflateEnd(&zi->ci.stream); + if (err == ZIP_OK) + err = tmp_err; + zi->ci.stream_initialised = 0; + } +#ifdef HAVE_BZIP2 + else if((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { + int tmperr = BZ2_bzCompressEnd(&zi->ci.bstream); + if (err==ZIP_OK) + err = tmperr; + zi->ci.stream_initialised = 0; + } +#endif + + if (!zi->ci.raw) + { + crc32 = (uLong)zi->ci.crc32; + uncompressed_size = zi->ci.totalUncompressedData; + } + compressed_size = zi->ci.totalCompressedData; + +# ifndef NOCRYPT + compressed_size += zi->ci.crypt_header_size; +# endif + + // update Current Item crc and sizes, + if(compressed_size >= 0xffffffff || uncompressed_size >= 0xffffffff || zi->ci.pos_local_header >= 0xffffffff) + { + /*version Made by*/ + zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)45,2); + /*version needed*/ + zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)45,2); + } + + zip64local_putValue_inmemory(zi->ci.central_header+16,crc32,4); /*crc*/ + + if(compressed_size >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+20, invalidValue,4); /*compr size*/ + else + zip64local_putValue_inmemory(zi->ci.central_header+20, compressed_size,4); /*compr size*/ + + /// set internal file attributes field + if (zi->ci.stream.data_type == Z_ASCII) + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)Z_ASCII,2); + + if(uncompressed_size >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+24, invalidValue,4); /*uncompr size*/ + else + zip64local_putValue_inmemory(zi->ci.central_header+24, uncompressed_size,4); /*uncompr size*/ + + // Add ZIP64 extra info field for uncompressed size + if(uncompressed_size >= 0xffffffff) + datasize += 8; + + // Add ZIP64 extra info field for compressed size + if(compressed_size >= 0xffffffff) + datasize += 8; + + // Add ZIP64 extra info field for relative offset to local file header of current file + if(zi->ci.pos_local_header >= 0xffffffff) + datasize += 8; + + if(datasize > 0) + { + char* p = NULL; + + if((uLong)(datasize + 4) > zi->ci.size_centralExtraFree) + { + // we can not write more data to the buffer that we have room for. + return ZIP_BADZIPFILE; + } + + p = zi->ci.central_header + zi->ci.size_centralheader; + + // Add Extra Information Header for 'ZIP64 information' + zip64local_putValue_inmemory(p, 0x0001, 2); // HeaderID + p += 2; + zip64local_putValue_inmemory(p, datasize, 2); // DataSize + p += 2; + + if(uncompressed_size >= 0xffffffff) + { + zip64local_putValue_inmemory(p, uncompressed_size, 8); + p += 8; + } + + if(compressed_size >= 0xffffffff) + { + zip64local_putValue_inmemory(p, compressed_size, 8); + p += 8; + } + + if(zi->ci.pos_local_header >= 0xffffffff) + { + zip64local_putValue_inmemory(p, zi->ci.pos_local_header, 8); + p += 8; + } + + // Update how much extra free space we got in the memory buffer + // and increase the centralheader size so the new ZIP64 fields are included + // ( 4 below is the size of HeaderID and DataSize field ) + zi->ci.size_centralExtraFree -= datasize + 4; + zi->ci.size_centralheader += datasize + 4; + + // Update the extra info size field + zi->ci.size_centralExtra += datasize + 4; + zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)zi->ci.size_centralExtra,2); + } + + if (err==ZIP_OK) + err = add_data_in_datablock(&zi->central_dir, zi->ci.central_header, (uLong)zi->ci.size_centralheader); + + free(zi->ci.central_header); + + if (err==ZIP_OK) + { + // Update the LocalFileHeader with the new values. + + ZPOS64_T cur_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream); + + if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_local_header + 14,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */ + + if(uncompressed_size >= 0xffffffff) + { + if(zi->ci.pos_zip64extrainfo > 0) + { + // Update the size in the ZIP64 extended field. + if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_zip64extrainfo + 4,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + + if (err==ZIP_OK) /* compressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, uncompressed_size, 8); + + if (err==ZIP_OK) /* uncompressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, compressed_size, 8); + } + } + else + { + if (err==ZIP_OK) /* compressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4); + + if (err==ZIP_OK) /* uncompressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4); + } + + if (ZSEEK64(zi->z_filefunc,zi->filestream, cur_pos_inzip,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + } + + zi->number_entry ++; + zi->in_opened_file_inzip = 0; + + return err; +} + +extern int ZEXPORT zipCloseFileInZip (zipFile file) +{ + return zipCloseFileInZipRaw (file,0,0); +} + +int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip) +{ + int err = ZIP_OK; + ZPOS64_T pos = zip64eocd_pos_inzip - zi->add_position_when_writting_offset; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDLOCHEADERMAGIC,4); + + /*num disks*/ + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + /*relative offset*/ + if (err==ZIP_OK) /* Relative offset to the Zip64EndOfCentralDirectory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, pos,8); + + /*total disks*/ /* Do not support spawning of disk so always say 1 here*/ + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)1,4); + + return err; +} + +int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) +{ + int err = ZIP_OK; + + uLong Zip64DataSize = 44; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDHEADERMAGIC,4); + + if (err==ZIP_OK) /* size of this 'zip64 end of central directory' */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)Zip64DataSize,8); // why ZPOS64_T of this ? + + if (err==ZIP_OK) /* version made by */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); + + if (err==ZIP_OK) /* version needed */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); + + if (err==ZIP_OK) /* number of this disk */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); + + if (err==ZIP_OK) /* total number of entries in the central dir */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); + + if (err==ZIP_OK) /* size of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)size_centraldir,8); + + if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ + { + ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (ZPOS64_T)pos,8); + } + return err; +} +int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) +{ + int err = ZIP_OK; + + /*signature*/ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4); + + if (err==ZIP_OK) /* number of this disk */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ + { + { + if(zi->number_entry >= 0xFFFF) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + } + } + + if (err==ZIP_OK) /* total number of entries in the central dir */ + { + if(zi->number_entry >= 0xFFFF) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + } + + if (err==ZIP_OK) /* size of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_centraldir,4); + + if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ + { + ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + if(pos >= 0xffffffff) + { + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)0xffffffff,4); + } + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)(centraldir_pos_inzip - zi->add_position_when_writting_offset),4); + } + + return err; +} + +int Write_GlobalComment(zip64_internal* zi, const char* global_comment) +{ + int err = ZIP_OK; + uInt size_global_comment = 0; + + if(global_comment != NULL) + size_global_comment = (uInt)strlen(global_comment); + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_global_comment,2); + + if (err == ZIP_OK && size_global_comment > 0) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream, global_comment, size_global_comment) != size_global_comment) + err = ZIP_ERRNO; + } + return err; +} + +extern int ZEXPORT zipClose (zipFile file, const char* global_comment) +{ + zip64_internal* zi; + int err = 0; + uLong size_centraldir = 0; + ZPOS64_T centraldir_pos_inzip; + ZPOS64_T pos; + + if (file == NULL) + return ZIP_PARAMERROR; + + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + } + +#ifndef NO_ADDFILEINEXISTINGZIP + if (global_comment==NULL) + global_comment = zi->globalcomment; +#endif + + centraldir_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream); + + if (err==ZIP_OK) + { + linkedlist_datablock_internal* ldi = zi->central_dir.first_block; + while (ldi!=NULL) + { + if ((err==ZIP_OK) && (ldi->filled_in_this_block>0)) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream, ldi->data, ldi->filled_in_this_block) != ldi->filled_in_this_block) + err = ZIP_ERRNO; + } + + size_centraldir += ldi->filled_in_this_block; + ldi = ldi->next_datablock; + } + } + free_linkedlist(&(zi->central_dir)); + + pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + if(pos >= 0xffffffff) + { + ZPOS64_T Zip64EOCDpos = ZTELL64(zi->z_filefunc,zi->filestream); + Write_Zip64EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); + + Write_Zip64EndOfCentralDirectoryLocator(zi, Zip64EOCDpos); + } + + if (err==ZIP_OK) + err = Write_EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); + + if(err == ZIP_OK) + err = Write_GlobalComment(zi, global_comment); + + if (ZCLOSE64(zi->z_filefunc,zi->filestream) != 0) + if (err == ZIP_OK) + err = ZIP_ERRNO; + +#ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(zi->globalcomment); +#endif + TRYFREE(zi); + + return err; +} + +extern int ZEXPORT zipRemoveExtraInfoBlock (char* pData, int* dataLen, short sHeader) +{ + char* p = pData; + int size = 0; + char* pNewHeader; + char* pTmp; + short header; + short dataSize; + + int retVal = ZIP_OK; + + if(pData == NULL || *dataLen < 4) + return ZIP_PARAMERROR; + + pNewHeader = (char*)ALLOC(*dataLen); + pTmp = pNewHeader; + + while(p < (pData + *dataLen)) + { + header = *(short*)p; + dataSize = *(((short*)p)+1); + + if( header == sHeader ) // Header found. + { + p += dataSize + 4; // skip it. do not copy to temp buffer + } + else + { + // Extra Info block should not be removed, So copy it to the temp buffer. + memcpy(pTmp, p, dataSize + 4); + p += dataSize + 4; + size += dataSize + 4; + } + } + + if(size < *dataLen) + { + // clean old extra info block. + memset(pData,0, *dataLen); + + // copy the new extra info block over the old + if(size > 0) + memcpy(pData, pNewHeader, size); + + // set the new extra info size + *dataLen = size; + + retVal = ZIP_OK; + } + else + retVal = ZIP_ERRNO; + + TRYFREE(pNewHeader); + + return retVal; +} \ No newline at end of file diff --git a/LifeCore/minizip/zip.h b/LifeCore/minizip/zip.h new file mode 100644 index 0000000..34be5cb --- /dev/null +++ b/LifeCore/minizip/zip.h @@ -0,0 +1,356 @@ +/* zip.h -- IO on .zip files using zlib +Version 1.1, February 14h, 2010 +part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + +Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + +Modifications for Zip64 support +Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + +For more info read MiniZip_info.txt + +--------------------------------------------------------------------------- + +Condition of use and distribution are the same than zlib : + +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. + +--------------------------------------------------------------------------- + +Changes + +See header of zip.h + +*/ + +#ifndef _zip12_H +#define _zip12_H + +#ifdef __cplusplus +extern "C" { +#endif + + //#define HAVE_BZIP2 + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#ifdef HAVE_BZIP2 +#include "bzlib.h" +#endif + +#define Z_BZIP2ED 12 + +#if defined(STRICTZIP) || defined(STRICTZIPUNZIP) + /* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ + typedef struct TagzipFile__ { int unused; } zipFile__; + typedef zipFile__ *zipFile; +#else + typedef voidp zipFile; +#endif + +#define ZIP_OK (0) +#define ZIP_EOF (0) +#define ZIP_ERRNO (Z_ERRNO) +#define ZIP_PARAMERROR (-102) +#define ZIP_BADZIPFILE (-103) +#define ZIP_INTERNALERROR (-104) + +#ifndef DEF_MEM_LEVEL +# if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +# else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +# endif +#endif + /* default memLevel */ + + /* tm_zip contain date/time info */ + typedef struct tm_zip_s + { + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ + } tm_zip; + + typedef struct + { + tm_zip tmz_date; /* date in understandable format */ + uLong dosDate; /* if dos_date == 0, tmu_date is used */ + /* uLong flag; */ /* general purpose bit flag 2 bytes */ + + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + } zip_fileinfo; + + typedef const char* zipcharpc; + +#define APPEND_STATUS_CREATE (0) +#define APPEND_STATUS_CREATEAFTER (1) +#define APPEND_STATUS_ADDINZIP (2) + + extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append)); + extern zipFile ZEXPORT zipOpen64 OF((const void *pathname, int append)); + /* + Create a zipfile. + pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on + an Unix computer "zlib/zlib113.zip". + if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip + will be created at the end of the file. + (useful if the file contain a self extractor code) + if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will + add files in existing zip (be sure you don't add file that doesn't exist) + If the zipfile cannot be opened, the return value is NULL. + Else, the return value is a zipFile Handle, usable with other function + of this zip package. + */ + + /* Note : there is no delete function into a zipfile. + If you want delete file into a zipfile, you must open a zipfile, and create another + Of couse, you can use RAW reading and writing to copy the file you did not want delte + */ + + extern zipFile ZEXPORT zipOpen2 OF((const char *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc_def* pzlib_filefunc_def)); + + extern zipFile ZEXPORT zipOpen2_64 OF((const void *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc64_def* pzlib_filefunc_def)); + + extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level)); + + extern int ZEXPORT zipOpenNewFileInZip64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int zip64)); + + /* + Open a file in the ZIP for writing. + filename : the filename in zip (if NULL, '-' without quote will be used + *zipfi contain supplemental information + if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local + contains the extrafield data the the local header + if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global + contains the extrafield data the the local header + if comment != NULL, comment contain the comment string + method contain the compression method (0 for store, Z_DEFLATED for deflate) + level contain the level of compression (can be Z_DEFAULT_COMPRESSION) + zip64 is set to 1 if a zip64 extended information block should be added to the local file header. + this MUST be '1' if the uncompressed size is >= 0xffffffff. + + */ + + extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw)); + + extern int ZEXPORT zipOpenNewFileInZip2_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int zip64)); + /* + Same than zipOpenNewFileInZip, except if raw=1, we write raw file + */ + + extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting)); + + extern int ZEXPORT zipOpenNewFileInZip3_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + int zip64 + )); + + /* + Same than zipOpenNewFileInZip2, except + windowBits,memLevel,,strategy : see parameter strategy in deflateInit2 + password : crypting password (NULL for no crypting) + crcForCrypting : crc of file to compress (needed for crypting) + */ + + extern int ZEXPORT zipOpenNewFileInZip4 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase + )); + + extern int ZEXPORT zipOpenNewFileInZip4_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase, + int zip64 + )); + /* + Same than zipOpenNewFileInZip4, except + versionMadeBy : value for Version made by field + flag : value for flag field (compression level info will be added) + */ + + extern int ZEXPORT zipWriteInFileInZip OF((zipFile file, + const void* buf, + unsigned len)); + /* + Write data in the zipfile + */ + + extern int ZEXPORT zipCloseFileInZip OF((zipFile file)); + /* + Close the current file in the zipfile + */ + + extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file, + uLong uncompressed_size, + uLong crc32)); + + extern int ZEXPORT zipCloseFileInZipRaw64 OF((zipFile file, + ZPOS64_T uncompressed_size, + uLong crc32)); + + /* + Close the current file in the zipfile, for file opened with + parameter raw=1 in zipOpenNewFileInZip2 + uncompressed_size and crc32 are value for the uncompressed size + */ + + extern int ZEXPORT zipClose OF((zipFile file, + const char* global_comment)); + /* + Close the zipfile + */ + + extern int ZEXPORT zipRemoveExtraInfoBlock OF((char* pData, int* dataLen, short sHeader)); + /* + zipRemoveExtraInfoBlock - Added by Mathias Svensson + + Remove extra information block from a extra information data for the local file header or central directory header + + It is needed to remove ZIP64 extra information blocks when before data is written if using RAW mode. + + 0x0001 is the signature header for the ZIP64 extra information blocks + + usage. + Remove ZIP64 Extra information from a central director extra field data + zipRemoveExtraInfoBlock(pCenDirExtraFieldData, &nCenDirExtraFieldDataLen, 0x0001); + + Remove ZIP64 Extra information from a Local File Header extra field data + zipRemoveExtraInfoBlock(pLocalHeaderExtraFieldData, &nLocalHeaderExtraFieldDataLen, 0x0001); + */ + +#ifdef __cplusplus +} +#endif + +#endif /* _zip64_H */ \ No newline at end of file diff --git a/LifeCore/tools/CompilerDetect.h b/LifeCore/tools/CompilerDetect.h new file mode 100644 index 0000000..6eb1576 --- /dev/null +++ b/LifeCore/tools/CompilerDetect.h @@ -0,0 +1,50 @@ +#ifndef CompilerDetect_h__ +#define CompilerDetect_h__ + +namespace LifeCore{ + +/*enum CompilerType{ + CT_GCC, + CT_Intel, + CT_MwCodewarrior, + CT_MinGw, + CT_BorlandCpp, + CT_Clang, + CT_DigitalMars, + CT_IbmXl, + CT_KeilCArm, + CT_AltiumMbC, + CT_AltiumCthw +}*/ + +#if defined(__GNUC__) +#define COMPILER_GCC +#elif defined(__INTEL_COMPILER) | (__ICC) | (__ECC) | (__ICL) +#define COMPILER_INTEL +#elif defined(__MWERKS__) | defined(__CWCC__) +#define COMPILER_MW_CODEWARRIOR +#elif defined(_MSC_VER) +#define COMPILER_MSVC +#elif defined(__MINGW32__) +#define COMPILER_MINGW +#define COMPILER_MINGW32 +#elif defined(__MINGW64__) +#define COMPILER_MINGW +#define COMPILER_MINGW64 +#elif defined(__BORLANDC__) | defined(__CODEGEARC__) +#define COMPILER_BORLAND_CPP +#elif defined(__clang__) +#define COMPILER_CLANG +#elif defined(__DMC__) +#define COMPILER_DIGITALMARS +#elif defined(__xlc__) | defined(__xlC__) | defined(__xlC_ver__) | defined(__IBMC__) | defined(__IBMCPP__) +#define COMPILER_IBM_XL +#elif defined(__CA__) | defined(__KEIL__) +#define COMPILER_KEIL_CARM +#elif defined(__CMB__) +#define COMPILER_ALTIUM_MB_C +#elif defined(__CHC__) +#define COMPILER_ALTIUM_CTHW +#endif +} +#endif // CompilerDetect_h__ diff --git a/LifeCore/tools/Inline.h b/LifeCore/tools/Inline.h new file mode 100644 index 0000000..aca0720 --- /dev/null +++ b/LifeCore/tools/Inline.h @@ -0,0 +1,18 @@ +#ifndef Inline_h__ +#define Inline_h__ + +#include "CompilerDetect.h" + +namespace LifeCore{ + +#if defined(COMPILER_MSVC) + #define FORCEINLINE __forceinline +#elif defined(COMPILER_GCC) + #define FORCEINLINE __attribute__((always_inline)) +#else + #define FORCEINLINE inline +#endif + +#define INLINE inline +} +#endif // Inline_h__ diff --git a/LifeCore/tools/OsDetect.h b/LifeCore/tools/OsDetect.h new file mode 100644 index 0000000..b640acc --- /dev/null +++ b/LifeCore/tools/OsDetect.h @@ -0,0 +1,26 @@ +#ifndef LOsDetect_h__ +#define LOsDetect_h__ + +#include "Inline.h" + +namespace LifeCore{ + +enum OsType{ + OsUnknown, + OsWindows, + OsLinux +}; + +#if defined(_WIN16) || defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__) +#define LOS_WINDOWS +INLINE OsType getOsType(){return OsWindows;} +#elif defined(__gnu_linux__) || defined(__linux__) +#define LOS_LINUX +INLINE OsType getOsType(){return OsLinux;} +#else +#define LOS_UNKNOWN +INLINE OsType getOsType(){return OsUnknown;} +#endif + +} +#endif // LOsDetect_h__ diff --git a/LifeCore/vfs/IContentProvider.h b/LifeCore/vfs/IContentProvider.h new file mode 100644 index 0000000..aa2b488 --- /dev/null +++ b/LifeCore/vfs/IContentProvider.h @@ -0,0 +1,22 @@ +#ifndef ICONTENTPROVIDER_H__ +#define ICONTENTPROVIDER_H__ + +#include "../IDataStream.h" + +namespace LifeCore +{ + class IFileSystem; + + class IContentProvider + { + public: + virtual ~IContentProvider(){}; + virtual void Close() = 0; + virtual IFileSystem* getFileSystem() = 0; + + protected: + IDataStreamPtr g_dataStream; + }; +} + +#endif \ No newline at end of file diff --git a/LifeCore/vfs/IFile.h b/LifeCore/vfs/IFile.h new file mode 100644 index 0000000..ac26813 --- /dev/null +++ b/LifeCore/vfs/IFile.h @@ -0,0 +1,52 @@ +#ifndef IFILE_H__ +#define IFILE_H__ + +#include "../IDataStream.h" +#include "../LString.h" +#include "IContentProvider.h" +#include "../Types.h" +#include "../delegate/Delegate.h" +namespace LifeCore +{ + enum FILE_ACCESS + { + FA_READ = 1, + FA_WRITE = 2, + FA_READ_WRITE = 3 + }; + + enum FILE_MODE + { + FM_CREATE_NEW = 1, + FM_CREATE = 2, + FM_OPEN = 3, + FM_OPEN_OR_CREATE = 4, + FM_TRUNCATE = 5, + FM_APPEND = 6, + }; + + class IFile + { + public: + EVENT1(FileEvent, LifeCore::IFile*); + FileEvent Event_EndReadAsync; + + int Size; + IContentProvider* Provider; + String FileName; + virtual ~IFile(){} + virtual bool ReadAsync(char* buffer, size_t count_read) + { + UNREFERENCED_PARAMETER(buffer); + UNREFERENCED_PARAMETER(count_read); + return false; + } + virtual IDataStreamPtr OpenRead() = 0; + virtual IDataStreamPtr OpenWrite(int mode) = 0; + virtual IDataStreamPtr OpenReadWrite(int mode) = 0; + protected: + _bool isOpen; + }; +} + +#endif \ No newline at end of file diff --git a/LifeCore/vfs/IFileSystem.cpp b/LifeCore/vfs/IFileSystem.cpp new file mode 100644 index 0000000..d2d855d --- /dev/null +++ b/LifeCore/vfs/IFileSystem.cpp @@ -0,0 +1,158 @@ +#include "IFileSystem.h" +#include "../log/CLog.h" + +namespace LifeCore +{ + /*IFolder* IFileSystem::GetFolder(const String& path) + { + String folder, rPath; + GetRootFolderS(path, folder, rPath); + + if(folder.length()<2) + return g_rootFolder; + else if(folder.length() <1) + return (IFolder*)0; + if(rPath.length()<2) + { + if (folder == g_rootFolder->Name) + return g_rootFolder; + else + return g_rootFolder->GetFolder(folder); + } + else + { + IFolder* f = g_rootFolder; + IFolder* t; + do + { + GetRootFolderS(rPath, folder, rPath); + t = f->GetFolder(folder); + if(t) + { + f = t; + } + else + { + return f; + } + } + while(rPath.length()>1); + return f; + } + } + + IFile* IFileSystem::GetFile(const String& path) + { + String dir = GetFilePath(path, String(_text("/"))); + String file = GetFileName(path, String(_text("/"))); + IFolder* f = GetFolder(dir); + if(f) + return f->GetFile(file); + return (IFile*)0; + }*/ + int IFileSystem::NewFile(const String& path, int size) + { + String dir = GetFilePath(path, String(_text("/"))); + String fileName = GetFileName(path, String(_text("/"))); + IFolder* f; + if(GetFolder(dir, &f) != SC_OK) + { + if(this->CreateFolder(dir) != SC_OK) + return SC_FAIL; + + if(GetFolder(dir, &f) != SC_OK) + return SC_FAIL; + } + + IFile* file; + if(f->GetFile(fileName, &file) != SC_OK) + f->NewFile(fileName, size); + + return SC_OK; + } + int IFileSystem::CreateFolder(const String& path) + { + String folder, rPath; + IFolder* f; + GetRootFolderS(path, folder, rPath); + if(folder == g_rootFolder->Name) + f = g_rootFolder; + else + return SC_FAIL; + + do + { + GetRootFolderS(rPath, folder, rPath); + if(folder == STR("")) + return SC_OK; + else + { + if(f->GetFolder(folder, &f) != SC_OK) + { + f = f->NewFolder(folder); + } + } + } while (rPath != STR("")); + + return SC_OK; + } + + int IFileSystem::GetFile(const String& path, IFile** file) + { + String dir = GetFilePath(path, String(_text("/"))); + String fileName = GetFileName(path, String(_text("/"))); + IFolder* f; + if(GetFolder(dir, &f) == SC_OK) + return f->GetFile(fileName, file); + + return SC_FAIL; + } + int IFileSystem::GetFolder(const String& path, IFolder** output) + { + String folder, rPath; + GetRootFolderS(path, folder, rPath); + + if(folder.length()<2) + { + (*output) = g_rootFolder; + return SC_OK; + } + else if(folder.length() < 1) + { + return SC_FAIL; + } + if(rPath.length()<2) + { + if (folder == g_rootFolder->Name) + { + (*output) = g_rootFolder; + return SC_OK; + } + else + { + return g_rootFolder->GetFolder(folder, output); + } + } + else + { + IFolder* f = g_rootFolder; + IFolder* t; + do + { + GetRootFolderS(rPath, folder, rPath); + if(f->GetFolder(folder, &t) == SC_OK) + { + f = t; + } + else + { + (*output) = f; + return SC_OK; + } + } + while(rPath.length() > 1); + (*output) = f; + return SC_OK; + } + } +} \ No newline at end of file diff --git a/LifeCore/vfs/IFileSystem.h b/LifeCore/vfs/IFileSystem.h new file mode 100644 index 0000000..f590584 --- /dev/null +++ b/LifeCore/vfs/IFileSystem.h @@ -0,0 +1,64 @@ +#ifndef IFILESYSTEM_H__ +#define IFILESYSTEM_H__ + +#include "../IDataStream.h" +#include "IContentProvider.h" +#include "../LString.h" +#include "IFolder.h" +#include "../Types.h" +#include +#include "../Error.h" + +namespace LifeCore +{ + class IFileSystem + { + public: + static const int IOBufferSize = 4096; + String StorageName; + int StorageID; + IFolder* GetRootFolder() + { + return g_rootFolder; + } + IFileSystem(IFolder* rootFolder) + { + g_rootFolder = rootFolder; + } + ~IFileSystem() + { + if(g_rootFolder) + delete g_rootFolder; + } + int FileExists(String path) + { + return 1; + } + int GetFile(const String& path, IFile** file); + int GetFolder(const String& path, IFolder** output); + + int NewFile(const String& path, int size); + int CreateFolder(const String& path); + + /*IFile* GetFile(const String& path); + IFolder* GetFolder(const String& path);*/ + + void ReadFile(const String& src, void* buffer) + { + IFile* f1; + + this->GetFile(src, &f1); + + IDataStreamPtr srcStm = f1->OpenRead(); + + srcStm->Read(buffer, srcStm->Length()); + srcStm->Close(); + //f1->Close(); + } + private: + LifeCore::IFolder* g_rootFolder; + std::vector g_folders; + byte g_ioBuffer[IOBufferSize]; + }; +} +#endif \ No newline at end of file diff --git a/LifeCore/vfs/IFolder.h b/LifeCore/vfs/IFolder.h new file mode 100644 index 0000000..bb93757 --- /dev/null +++ b/LifeCore/vfs/IFolder.h @@ -0,0 +1,120 @@ +#ifndef IFOLDER_H__ +#define IFOLDER_H__ + +#include "../IDataStream.h" +#include "../LString.h" +#include "IFile.h" +#include +#include "../Error.h" +#include "../log/CLog.h" + +namespace LifeCore +{ + class IFolder + { + public: + IFolder() + :g_parent(0) + {folderCount=0; + fileCount=0; + } + virtual ~IFolder(void){} + virtual IFolder* NewFolder(const String& name) = 0; + virtual IFile* NewFile(const String& name, int size) = 0; + + int ID; + String Name; + int folderCount, fileCount; + IFolder* getParent(){return g_parent;} + void setParent(IFolder* parent){g_parent = parent;} + String getPath() + { + if (g_parent) + return g_parent->getPath() + _text("/") + this->Name; + else + return this->Name; + } + void AddFolder(IFolder* f) + { + if (f) + { + //g_folderCollection.insert(FolderPair(f->Name,f)); + g_folderCollection[f->Name] = f; + f->setParent(this); + folderCount++; + } + } + void AddFile(IFile* file) + { + g_fileCollection[file->FileName] = file; + fileCount++; + //file->setParent(this); + } + bool FileExists(const String& name) + { + FileMapIterator it = g_fileCollection.find(name); + + return (it != g_fileCollection.end()); + } + bool FolderExists(const String& name) + { + FolderMapIterator it = g_folderCollection.find(name); + + return (it != g_folderCollection.end()); + } + int GetFolder(const String& name, LifeCore::IFolder** output) + { + LifeCore::IFolder* f = g_folderCollection[name]; + if(f) + { + (*output) = f; + return SC_OK; + } + return SC_FAIL; + } + LifeCore::IFolder* GetFolder(int index) + { + FolderMap::iterator it = g_folderCollection.begin(); + std::advance(it, index); + return it->second; + } + + IFile* GetFile(int index) + { + FileMap::iterator it = g_fileCollection.begin(); + std::advance(it, index); + return it->second; + } + + int GetFile(const String& name, IFile** output) + { + IFile* file = g_fileCollection[name]; + if(file) + { + (*output) = file; + return SC_OK; + } + (*output) = 0; + /*String msg = STR("Could not find file ") + name; + //LOG_ERROR(msg,) + CLog::me()->Write(msg); + system("pause"); + exit(0);*/ + return SC_FAIL; + } + + protected: + typedef std::map FileMap; + typedef std::pair FilePair; + typedef std::map::const_iterator FileMapIterator; + + typedef std::map FolderMap; + typedef std::pair FolderPair; + typedef std::map::const_iterator FolderMapIterator; + FileMap g_fileCollection; + FolderMap g_folderCollection; + IFolder* g_parent; + }; +} + +#endif \ No newline at end of file diff --git a/LifeCore/vfs/RootFolder.h b/LifeCore/vfs/RootFolder.h new file mode 100644 index 0000000..bca11b1 --- /dev/null +++ b/LifeCore/vfs/RootFolder.h @@ -0,0 +1,19 @@ +#ifndef ROOT_FOLDER_H__ +#define ROOT_FOLDER_H__ + +#include "vfs/IFolder.h" +#include "log/CLog.h" + +namespace LifeCore +{ + class RootFolder : public IFolder + { + public: + RootFolder(){} + ~RootFolder(void){} + IFolder* NewFolder(const String& name){printf("Can not create folder in root! Folder: %s\n", name.c_str()); return 0;}; + IFile* NewFile(const String& name, int size) {printf("Can not create file in root! File: %s\n", name.c_str());return 0;}; + }; +} + +#endif \ No newline at end of file diff --git a/LifeCore/vfs/WinFileStream.cpp b/LifeCore/vfs/WinFileStream.cpp new file mode 100644 index 0000000..6d95d27 --- /dev/null +++ b/LifeCore/vfs/WinFileStream.cpp @@ -0,0 +1,57 @@ +#include "WinFileStream.h" + +namespace LifeCore +{ + WinFileStream::WinFileStream(const String& path) + :hFile(0) + { + hFile = CreateFileA(path.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + } + WinFileStream::~WinFileStream() + { + if(hFile) + { + CloseHandle(hFile); + } + } + void WinFileStream::Close() + { + CloseHandle(hFile); + hFile = NULL; + } + size_t WinFileStream::Read(void* pBuffer, size_t countBytes) + { + unsigned long countRead = 0; + ReadFile(hFile, pBuffer, countBytes, &countRead, NULL); + return (int)countRead; + } + size_t WinFileStream::Write(const void* pBuffer, size_t countBytes) + { + unsigned long countWrite = 0; + WriteFile(hFile, pBuffer, countBytes, &countWrite, NULL); + return (int)countWrite; + } + void WinFileStream::Seek(int position) + { + SetFilePointer(hFile, position, NULL, SEEK_SET); + } + size_t WinFileStream::Tell() + { + return SetFilePointer(hFile, 0, NULL, SEEK_CUR); + } + size_t WinFileStream::Length() + { + int pos = Tell(); + int length = SetFilePointer(hFile, 0, NULL, SEEK_END); + SetFilePointer(hFile, pos, NULL, SEEK_SET); + return length; + } + bool WinFileStream::CanRead() + { + return true; + } + bool WinFileStream::CanWrite() + { + return true; + } +} \ No newline at end of file diff --git a/LifeCore/vfs/WinFileStream.h b/LifeCore/vfs/WinFileStream.h new file mode 100644 index 0000000..ea3781f --- /dev/null +++ b/LifeCore/vfs/WinFileStream.h @@ -0,0 +1,25 @@ +#ifndef WinFileStream_h__ +#define WinFileStream_h__ +#include "../IDataStream.h" +#include "../LString.h" + +namespace LifeCore +{ + class WinFileStream : public IDataStream + { + public: + WinFileStream(const String& path); + ~WinFileStream(); + void Close(); + size_t Read(void* pBuffer, size_t countBytes); + size_t Write(const void* pBuffer, size_t countBytes); + void Seek(int position); + size_t Tell(); + size_t Length(); + bool CanRead(); + bool CanWrite(); + private: + HANDLE hFile; + }; +} +#endif // WinFileStream_h__ \ No newline at end of file diff --git a/LifeCore/vfs/fs_win/IFSContentProvider.cpp b/LifeCore/vfs/fs_win/IFSContentProvider.cpp new file mode 100644 index 0000000..8bef759 --- /dev/null +++ b/LifeCore/vfs/fs_win/IFSContentProvider.cpp @@ -0,0 +1,68 @@ +#include "IFSContentProvider.h" +#include "../../log/CLog.h" +namespace LifeCore +{ + static const String pakPathSplitter(_text("\\")); + + IFSContentProvider::IFSContentProvider(const String& file) + { + IFSFolder* folder = new IFSFolder(); + g_rootFolder = file; + folder->Provider = this; + folder->Name = GetDirectoryName(file); + + folder->EntryName = file; + + fileSystem = new IFileSystem(folder); + + IFolder* root = fileSystem->GetRootFolder(); + + //printf("%s", file.c_str()); + ScanDir(file, root); + } + IFSContentProvider::~IFSContentProvider() + { + if(fileSystem) + delete fileSystem; + } + void IFSContentProvider::ScanDir( String path, IFolder* directory) + { + WIN32_FIND_DATA FindData; + String modified_address = path; + DWORD tmp = 0; + modified_address += _text("*"); + HANDLE Handle = FindFirstFile(modified_address.c_str() , &FindData); + while( FindNextFile(Handle, &FindData) ) + { + String file_name = FindData.cFileName; + if( FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) + { + if(STRCMP(FindData.cFileName, _text(".."))) + { + String new_path = path; + new_path += file_name; + new_path += _text("\\"); + + IFSFolder* folder = new IFSFolder(); + folder->Provider = this; + folder->Name = file_name; + folder->EntryName = new_path; + directory->AddFolder(folder); + ScanDir( new_path, folder ); + } + } + else + { + String filepath = path + file_name; + HANDLE hf = CreateFile(filepath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + IFSFile* file = new IFSFile(); + file->FileName = file_name; + file->Provider = this; + file->Size = GetFileSize(hf,&tmp); + file->EntryName = filepath; + directory->AddFile(file); + CloseHandle(hf); + } + } + } +} \ No newline at end of file diff --git a/LifeCore/vfs/fs_win/IFSContentProvider.h b/LifeCore/vfs/fs_win/IFSContentProvider.h new file mode 100644 index 0000000..c0fe5ec --- /dev/null +++ b/LifeCore/vfs/fs_win/IFSContentProvider.h @@ -0,0 +1,26 @@ +#ifndef IFSCONTENTPROVIDER_H__ +#define IFSCONTENTPROVIDER_H__ + +#include "../IContentProvider.h" +#include "../../LString.h" +#include "../IFileSystem.h" +#include "IFSFolder.h" +#include + +namespace LifeCore +{ + class IFSContentProvider : public IContentProvider + { + public: + ~IFSContentProvider(); + void Close(){} + IFileSystem* getFileSystem(){return fileSystem;} + IFSContentProvider(const String& file); + String g_rootFolder; + IFileSystem* fileSystem; + + private: + void ScanDir( String path, IFolder* directory); + }; +} +#endif \ No newline at end of file diff --git a/LifeCore/vfs/fs_win/IFSFile.cpp b/LifeCore/vfs/fs_win/IFSFile.cpp new file mode 100644 index 0000000..4d9c8f0 --- /dev/null +++ b/LifeCore/vfs/fs_win/IFSFile.cpp @@ -0,0 +1,62 @@ +#include "IFSFile.h" +#include "IFSContentProvider.h" + +namespace LifeCore +{ + IFSFile::IFSFile() + { + } + IFSFile::IFSFile(const String& path, int size) + { + isOpen = false; + EntryName = path; + HANDLE file = CreateFileA(path.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if(GetLastError() != ERROR_ALREADY_EXISTS) + { + if(size > 0) + { + SetFilePointer(file, size - 1, 0, FILE_BEGIN); + char data = 0; + DWORD numWrite = 0; + WriteFile(file, &data, 1, &numWrite, NULL); + } + } + CloseHandle(file); + } + + bool IFSFile::ReadAsync(char* buffer, size_t count_read) + { + UNREFERENCED_PARAMETER(buffer); + UNREFERENCED_PARAMETER(count_read); + //TODO read! + return true; + } + IDataStreamPtr IFSFile::OpenWrite(int mode) + { + UNREFERENCED_PARAMETER(mode); + g_stream = new IFSStream(EntryName, FA_WRITE, this->Size); + return IDataStreamPtr(g_stream); + } + IDataStreamPtr IFSFile::OpenReadWrite(int mode) + { + UNREFERENCED_PARAMETER(mode); + return IDataStreamPtr(0); + //throw new NotImplementedException(); + } + + IDataStreamPtr IFSFile::OpenRead() + { + g_stream = new IFSStream(EntryName, FA_READ, this->Size); + return IDataStreamPtr(g_stream); + //g_stream = new IZipStream((this->Provider as IZipFileContentProvider).GetSource(), EntryName, FA_READ); + //return g_stream; + } + void IFSFile::Close() + { + if(g_stream) + { + g_stream->Close(); + delete g_stream; + } + } +} \ No newline at end of file diff --git a/LifeCore/vfs/fs_win/IFSFile.h b/LifeCore/vfs/fs_win/IFSFile.h new file mode 100644 index 0000000..c0ae77a --- /dev/null +++ b/LifeCore/vfs/fs_win/IFSFile.h @@ -0,0 +1,29 @@ +#ifndef IFSFILE_H__ +#define IFSFILE_H__ + +//#include "IDataStream.h" +#include "../../LString.h" +#include "../IFile.h" +#include "IFSStream.h" + +namespace LifeCore +{ + class IFSFile : public IFile + { + public: + IFSFile(); + IFSFile(const String& path, int size); + ~IFSFile(){} + String EntryName; + bool ReadAsync(char* buffer, size_t count_read); + IDataStreamPtr OpenWrite(int mode); + IDataStreamPtr OpenReadWrite(int mode); + IDataStreamPtr OpenRead(); + void Close(); + private: + int offsetIndex; + IFSStream* g_stream; + }; +} + +#endif \ No newline at end of file diff --git a/LifeCore/vfs/fs_win/IFSFolder.cpp b/LifeCore/vfs/fs_win/IFSFolder.cpp new file mode 100644 index 0000000..2d57faf --- /dev/null +++ b/LifeCore/vfs/fs_win/IFSFolder.cpp @@ -0,0 +1,53 @@ +#include "IFSFolder.h" + +namespace LifeCore +{ + IFSFolder::~IFSFolder(void) + { + for( FolderMap::iterator it = g_folderCollection.begin(); it != g_folderCollection.end(); it++ ) + delete it->second; + } + IFSFolder::IFSFolder() + + { + } + IFSFolder::IFSFolder(const String& folderName, const String& entryName) + { + this->Name = folderName; + this->EntryName = entryName; + + CreateDirectoryA(EntryName.c_str(), NULL); + } + IFolder* IFSFolder::NewFolder(const String& name) + { + IFolder* test; + if(this->GetFolder(name, &test) != SC_OK) + { + IFSFolder* newFolder = new IFSFolder(name, this->EntryName + name + _text("\\")); + newFolder->setParent(this); + newFolder->Provider = this->Provider; + this->AddFolder(newFolder); + return newFolder; + } + else + { + return test; + } + } + IFile* IFSFolder::NewFile(const String& name, int size) + { + IFile* f; + if(this->GetFile(name, &f) != SC_OK) + { + IFSFile* newFile = new IFSFile(this->EntryName + name, size); + newFile->FileName = name; + newFile->Provider = this->Provider; + this->AddFile(newFile); + return newFile; + } + else + { + return f; + } + } +} \ No newline at end of file diff --git a/LifeCore/vfs/fs_win/IFSFolder.h b/LifeCore/vfs/fs_win/IFSFolder.h new file mode 100644 index 0000000..7a159cb --- /dev/null +++ b/LifeCore/vfs/fs_win/IFSFolder.h @@ -0,0 +1,26 @@ +#ifndef IFSFOLDER_H__ +#define IFSFOLDER_H__ + +#include "../../IDataStream.h" +#include "../../LString.h" +#include "../IFolder.h" +#include "IFSFile.h" +#include "../IContentProvider.h" +#include + +namespace LifeCore +{ + class IFSFolder : public IFolder + { + public: + IFSFolder(); + IFSFolder(const String& folderName, const String& entryName); + ~IFSFolder(void); + IContentProvider* Provider; + String EntryName; + IFolder* NewFolder(const String& name); + IFile* NewFile(const String& name, int size); + }; +} + +#endif \ No newline at end of file diff --git a/LifeCore/vfs/fs_win/IFSStream.h b/LifeCore/vfs/fs_win/IFSStream.h new file mode 100644 index 0000000..2f21c5f --- /dev/null +++ b/LifeCore/vfs/fs_win/IFSStream.h @@ -0,0 +1,86 @@ +#ifndef IFSSTREAM_H__ +#define IFSSTREAM_H__ + +#include "../../IDataStream.h" +#include "../../LString.h" +#include +#include + +namespace LifeCore +{ + class IFSStream : public IDataStream + { + private: + int openMode; + size_t stmLength; + HANDLE pFile; + public: + ~IFSStream() + { + //printf("DESTRUCTING WIN FILE STREAM\n"); + Close(); + } + void Close() + { + //printf("CLOSING WIN FILE STREAM\n"); + if(pFile) + CloseHandle(pFile); + pFile = (HANDLE)0; + } + IFSStream(String& path, int fileAccessMode, size_t size) + { + UNREFERENCED_PARAMETER(size); + this->stmLength = 0; + openMode = fileAccessMode; + if (fileAccessMode == FA_READ) + { + pFile = CreateFileA(path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + } + else + { + pFile = CreateFileA(path.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + } + + this->stmLength = SetFilePointer(pFile, 0, NULL, FILE_END); + SetFilePointer(pFile, 0, NULL, FILE_BEGIN); + } + size_t Read(void* pBuffer, size_t countBytes) + { + DWORD numBytes = 0; + + ReadFile(pFile,pBuffer,countBytes, &numBytes, NULL); + return numBytes; + } + size_t Write(const void* pBuffer, size_t countBytes) + { + DWORD numBytes = 0; + WriteFile(pFile,pBuffer,countBytes,&numBytes, NULL); + return numBytes; + } + void Seek(size_t position) + { + SetFilePointer(pFile, position, NULL, FILE_BEGIN); + } + size_t Tell() + { + return SetFilePointer(pFile, 0, NULL, FILE_CURRENT); + } + size_t Length() + { + int pos = Tell(); + int length = SetFilePointer(pFile, 0, NULL, SEEK_END); + SetFilePointer(pFile, pos, NULL, SEEK_SET); + return length; + } + bool CanRead() + { + return true; + } + bool CanWrite() + { + return true; + } + }; +} + +#endif \ No newline at end of file diff --git a/LifeCore/vfs/pak/IPakFile.cpp b/LifeCore/vfs/pak/IPakFile.cpp new file mode 100644 index 0000000..2d1830e --- /dev/null +++ b/LifeCore/vfs/pak/IPakFile.cpp @@ -0,0 +1,31 @@ +#include "IPakFile.h" +#include "IPakFileContentProvider.h" + +namespace LifeCore +{ + IPakFile::IPakFile() + { + isOpen = false; + } + IDataStreamPtr IPakFile::OpenWrite(int mode) + { + printf("ow->mode: %i\n", mode); + g_stream = new IPakStream(&((IPakFileContentProvider*)this->Provider)->g_sourceFile, EntryName, FA_WRITE); + return IDataStreamPtr(g_stream); + } + IDataStreamPtr IPakFile::OpenReadWrite(int mode) + { + printf("or->mode: %i\n", mode); + return IDataStreamPtr(0); + //throw new NotImplementedException(); + } + + IDataStreamPtr IPakFile::OpenRead() + { + IPakFileContentProvider* prov = (IPakFileContentProvider*)this->Provider; + g_stream = new IPakStream(&prov->g_sourceFile, EntryName, FA_READ); + return IDataStreamPtr(g_stream); + //g_stream = new IZipStream((this->Provider as IZipFileContentProvider).GetSource(), EntryName, FA_READ); + //return g_stream; + } +} \ No newline at end of file diff --git a/LifeCore/vfs/pak/IPakFile.h b/LifeCore/vfs/pak/IPakFile.h new file mode 100644 index 0000000..0b4dc51 --- /dev/null +++ b/LifeCore/vfs/pak/IPakFile.h @@ -0,0 +1,26 @@ +#ifndef IPAKFILE_H__ +#define IPAKFILE_H__ + +//#include "IDataStream.h" +#include "../../LString.h" +#include "../IFile.h" +#include "IPakStream.h" + +namespace LifeCore +{ + class IPakFile : public IFile + { + public: + IPakFile(); + ~IPakFile(){} + String EntryName; + IDataStreamPtr OpenWrite(int mode); + IDataStreamPtr OpenReadWrite(int mode); + IDataStreamPtr OpenRead(); + private: + int offsetIndex; + IPakStream* g_stream; + }; +} + +#endif \ No newline at end of file diff --git a/LifeCore/vfs/pak/IPakFileContentProvider.cpp b/LifeCore/vfs/pak/IPakFileContentProvider.cpp new file mode 100644 index 0000000..8afc441 --- /dev/null +++ b/LifeCore/vfs/pak/IPakFileContentProvider.cpp @@ -0,0 +1,76 @@ +#include "IPakFileContentProvider.h" + +namespace LifeCore +{ + static const String pakPathSplitter(_text("/")); + + IPakFileContentProvider::IPakFileContentProvider(IDataStream* pakFile, const String& name) + { + IPakFolder* folder = new IPakFolder(); + // g_rootFolder = pakFile; + folder->Provider = this; + folder->Name = name; + + folder->EntryName = _text(""); + + fileSystem = new IFileSystem(folder); + + g_sourceFile.Open(pakFile); + + fileSystem->GetRootFolder(); + BuildTree(); + } + void IPakFileContentProvider::BuildTree() + { + //g_sourceFile.LocateEntry(0); + int numFiles = g_sourceFile.filesCount(); + + for (int i = 0; i < numFiles; i++) + { + //g_sourceFile.LocateEntry(i); + entry_info info = g_sourceFile.GetEntryInfo(i); + + String p = GetPakPathWithoutFilename(info.fileName); + + IPakFolder* folder; + + if(this->fileSystem->GetFolder(p, (IFolder**)&folder) != SC_OK) + { + printf("Failed to get folder! IPakFileContentProvider::BuildTree\n"); + return; + } + + IPakFile* file = new IPakFile(); + file->FileName = GetFileName(info.fileName, pakPathSplitter); + file->Provider = this; + file->Size = info.fileSize; + file->EntryName = info.fileName; + folder->AddFile(file); + + //g_sourceFile.CloseCurrentEntry(); + } + + //print folder tree + String tab(_text("\t")); + int cnt = fileSystem->GetRootFolder()->folderCount; + for(int i = 0; i < cnt; i++) + { + IFolder* fld = fileSystem->GetRootFolder()->GetFolder(i); + PRINT(_text("%s\n"), fld->Name.c_str()); + PRINT(_text("Files count: %i\n"), fld->fileCount); + } + } + + String IPakFileContentProvider::GetPakPathWithoutFilename(const String& path) + { + int offset = path.find_last_of(_text("/")); + if(offset>=0) + { + return path.substr(0, offset); + } + else + { + return _text("/"); + } + } +} \ No newline at end of file diff --git a/LifeCore/vfs/pak/IPakFileContentProvider.h b/LifeCore/vfs/pak/IPakFileContentProvider.h new file mode 100644 index 0000000..f66a3ec --- /dev/null +++ b/LifeCore/vfs/pak/IPakFileContentProvider.h @@ -0,0 +1,28 @@ +#ifndef IPAKFILECONTENTPROVIDER_H__ +#define IPAKFILECONTENTPROVIDER_H__ + +#include "../IContentProvider.h" +#include "../../LString.h" +#include "../IFileSystem.h" +#include "pak_io/PakFile.h" +#include "IPakFolder.h" + +namespace LifeCore +{ + class IPakFileContentProvider : public IContentProvider + { + public: + ~IPakFileContentProvider(){}; + void Close(){} + IFileSystem* getFileSystem(){return fileSystem;} + IPakFileContentProvider(IDataStream* pakFile, const String& name); + String g_rootFolder; + PakFile g_sourceFile; + IFileSystem* fileSystem; + + private: + void BuildTree(); + String GetPakPathWithoutFilename(const String& path); + }; +} +#endif \ No newline at end of file diff --git a/LifeCore/vfs/pak/IPakFolder.cpp b/LifeCore/vfs/pak/IPakFolder.cpp new file mode 100644 index 0000000..2b10a79 --- /dev/null +++ b/LifeCore/vfs/pak/IPakFolder.cpp @@ -0,0 +1,31 @@ +#include "IPakFolder.h" + +namespace LifeCore +{ + IPakFolder::~IPakFolder(void) + { + for( FolderMap::iterator it = g_folderCollection.begin(); it != g_folderCollection.end(); it++ ) + delete it->second; + } + IFolder* IPakFolder::NewFolder(const String& name) + { + IPakFolder* newFolder = new IPakFolder(); + newFolder->Name = name; + newFolder->setParent(this); + newFolder->EntryName = this->EntryName + name + _text("/"); + newFolder->Provider = this->Provider; + this->AddFolder(newFolder); + return newFolder; + } + IFile* IPakFolder::NewFile(const String& name, int size) + { + UNREFERENCED_PARAMETER(size); + IFile* newFile = new IPakFile(); + newFile->FileName = name; + newFile->Provider = this->Provider; + IPakFile* xf = (IPakFile*)newFile; + xf->EntryName = this->EntryName + name; + this->AddFile(newFile); + return newFile; + } +} \ No newline at end of file diff --git a/LifeCore/vfs/pak/IPakFolder.h b/LifeCore/vfs/pak/IPakFolder.h new file mode 100644 index 0000000..d655a88 --- /dev/null +++ b/LifeCore/vfs/pak/IPakFolder.h @@ -0,0 +1,24 @@ +#ifndef IPAKFOLDER_H__ +#define IPAKFOLDER_H__ + +#include "../../IDataStream.h" +#include "../../LString.h" +#include "../IFolder.h" +#include "IPakFile.h" +#include "../IContentProvider.h" +#include + +namespace LifeCore +{ + class IPakFolder : public IFolder + { + public: + ~IPakFolder(void); + IContentProvider* Provider; + String EntryName; + IFolder* NewFolder(const String& name); + IFile* NewFile(const String& name, int size); + }; +} + +#endif \ No newline at end of file diff --git a/LifeCore/vfs/pak/IPakStream.h b/LifeCore/vfs/pak/IPakStream.h new file mode 100644 index 0000000..5bad485 --- /dev/null +++ b/LifeCore/vfs/pak/IPakStream.h @@ -0,0 +1,77 @@ +#ifndef IPAKSTREAM_H__ +#define IPAKSTREAM_H__ + +#include "../../IDataStream.h" +#include "../../LString.h" +#include +#include "pak_io/PakFile.h" + +namespace LifeCore +{ + class IPakStream : public IDataStream + { + private: + PakFile* source; + entry_info info; + int openMode; + size_t stmLength; + PakEntryHandle entry; + public: + ~IPakStream(){} + void Close() + { + source->fclose(entry); + openMode = 0; + } + IPakStream(PakFile* file, String& entryName, int fileAccessMode) + { + source = file; + openMode = fileAccessMode; + // source->CloseCurrentEntry(); + if (fileAccessMode == FA_READ) + { + entry = source->fopen(entryName); + // source->LocateEntry(entry); + //info = source->GetEntryInfo(); + stmLength = source->fget_size(entry); + //source->OpenCurrentEntry(); + } + else + { + throw "Creation of new PAK entry is not implemented! IPakStream.h"; + //source->NewFileEntry(entry); + } + } + size_t Read(void* pBuffer, size_t countBytes) + { + return source->fread(entry, pBuffer, countBytes); + } + size_t Write(const void* pBuffer, size_t countBytes) + { + source->fwrite(entry, pBuffer, countBytes); + return countBytes; + } + void Seek(size_t position) + { + source->fseek(entry, position); + } + size_t Tell() + { + return source->ftell(entry); + } + size_t Length() + { + return source->fget_size(entry); + } + bool CanRead() + { + return true; + } + bool CanWrite() + { + return true; + } + }; +} + +#endif \ No newline at end of file diff --git a/LifeCore/vfs/pak/pak_io/EntryInfo.h b/LifeCore/vfs/pak/pak_io/EntryInfo.h new file mode 100644 index 0000000..e70e801 --- /dev/null +++ b/LifeCore/vfs/pak/pak_io/EntryInfo.h @@ -0,0 +1,13 @@ +#ifndef ENTRY_INFO_H__ +#define ENTRY_INFO_H__ +#include "PakTypes.h" + +#define MAX_ENTRY_NAME_LENGTH 0x100 + +typedef struct entry_info +{ + int fileSize; + tchar fileName[MAX_ENTRY_NAME_LENGTH]; +}entry_info; + +#endif \ No newline at end of file diff --git a/LifeCore/vfs/pak/pak_io/FileEntry.h b/LifeCore/vfs/pak/pak_io/FileEntry.h new file mode 100644 index 0000000..f301e30 --- /dev/null +++ b/LifeCore/vfs/pak/pak_io/FileEntry.h @@ -0,0 +1,11 @@ +#include "../../../LString.h" + +#define MAX_ENTRY_NAME_LENGTH 0x100 + +typedef struct FileEntry +{ + int parentIndex; + int fileSize; + int fileOffset; + tchar name[MAX_ENTRY_NAME_LENGTH]; +}FileEntry, *PFileEntry, **LPFileEntry; \ No newline at end of file diff --git a/LifeCore/vfs/pak/pak_io/FolderEntry.h b/LifeCore/vfs/pak/pak_io/FolderEntry.h new file mode 100644 index 0000000..aac500f --- /dev/null +++ b/LifeCore/vfs/pak/pak_io/FolderEntry.h @@ -0,0 +1,6 @@ +#define MAX_ENTRY_NAME_LENGTH 0x100 + +typedef struct FolderEntry +{ + wchar_t name[MAX_ENTRY_NAME_LENGTH]; +}FolderEntry; \ No newline at end of file diff --git a/LifeCore/vfs/pak/pak_io/PakFile.cpp b/LifeCore/vfs/pak/pak_io/PakFile.cpp new file mode 100644 index 0000000..9175ff9 --- /dev/null +++ b/LifeCore/vfs/pak/pak_io/PakFile.cpp @@ -0,0 +1,312 @@ +#include "PakFile.h" +#include +#include "../../../log/CLog.h" +namespace LifeCore +{ + PakFile::PakFile(void) + :openEntriesCount(0) + { + currentMode = PM_IDLE; + memset(&openEntries[0], 0, sizeof(OpenEntryInfo) * PAK_MAX_OPEN_ENTRY); + } + + PakFile::~PakFile(void) + { + } + + int PakFile::Open(IDataStream* source) + { + curEntryId = 0; + pFile = source; + // + // pFile = CreateFileA(path.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if(pFile->Length() == 0) + { + //write default header to created file + header.version = PAK_VERSION; + header.whiteSpacesCount = 0; + header.filesCount = 0; + header.foldersCount = 0; + header.lastFolderId = 0; + header.dataChunkLength = 0; + header.extraDataOffset = sizeof(PakHeader); + + pFile->Seek(header.extraDataOffset); + // SetFilePointer(pFile, header.extraDataOffset, NULL, FILE_BEGIN); + + freeFileSlots = 16; + hFiles = (FileEntry*)malloc(sizeof(FileEntry) * 16); + tmpEntry = hFiles; + } + else + { + DWORD numWrite = 0; + //read file header + numWrite = pFile->Read(&header,sizeof(PakHeader)); + //ReadFile(pFile,&header,sizeof(PakHeader), &numWrite, NULL); + + if(header.version != PAK_VERSION) + { + //CloseHandle(pFile); + //pFile->Close(); + return PAK_BAD_FILE; + } + + pFile->Seek(header.extraDataOffset); + + //read folder entries + int cntAlloc; + cntAlloc = header.foldersCount > 0 ? header.foldersCount : 16; + freeFolderSlots = header.foldersCount > 0 ? 0 : 16; + hFolders = (FolderEntry*)malloc(sizeof(FolderEntry) * cntAlloc); + + pFile->Read(hFolders, sizeof(FolderEntry) * header.foldersCount); + //ReadFile(pFile, hFolders, sizeof(FolderEntry) * header.foldersCount, &numWrite, NULL); + + //read file entries + cntAlloc = header.filesCount > 0 ? header.filesCount : 16; + freeFileSlots = header.filesCount > 0 ? 0 : 16; + hFiles = (FileEntry*)malloc(sizeof(FileEntry) * cntAlloc); + + pFile->Read(hFiles, sizeof(FileEntry) * header.filesCount); + //ReadFile(pFile, hFiles, sizeof(FileEntry) * header.filesCount, &numWrite, NULL); + + tmpEntry = hFiles; + } + return PAK_OK; + } + + void PakFile::DumpExtraData() + { + pFile->Seek(sizeof(PakHeader) + header.dataChunkLength); + if(header.foldersCount > 0){ + pFile->Write(hFolders, sizeof(FolderEntry) * header.foldersCount); + } + + if(header.filesCount > 0) + { + pFile->Write(hFiles, sizeof(FileEntry) * header.filesCount); + } + } + + void PakFile::DumpHeader() + { + DWORD numWrite = 0; + pFile->Seek(0); + //SetFilePointer(pFile, 0, NULL, FILE_BEGIN); + //WriteFile(pFile, &header, sizeof(PakHeader), &numWrite, NULL); + numWrite = pFile->Write(&header, sizeof(PakHeader)); + } + + void PakFile::NewFolderEntry(const String& path) + { + CLog::me()->Write(path); + /*FolderEntry newEntry; + memset(newEntry.name,0,sizeof(wchar_t) * MAX_ENTRY_NAME_LENGTH); + path.copy(newEntry.name, path.length()); + FolderCollection[path] = newEntry; + header.foldersCount++;*/ + } + PakEntryHandle PakFile::fopen(const String& name) + { + if(openEntriesCount >= PAK_MAX_OPEN_ENTRY) + return -1; + + if(LocateEntry(name) != PAK_OK) + return -1; + + openEntries[openEntriesCount].entry = tmpEntry; + openEntries[openEntriesCount].position = 0; + + openEntriesCount++; + + return openEntriesCount - 1; + } + int PakFile::fread(PakEntryHandle entry, void* buffer, int length) + { + if(entry < 0 || entry >= PAK_MAX_OPEN_ENTRY) + return -1; + + OpenEntryInfo* info = &openEntries[entry]; + + int maxCnt = info->entry->fileSize - info->position; + if(!length || !maxCnt) + return 0; + if(length > maxCnt) + length = maxCnt; + + //SetFilePointer(pFile, info->entry->fileOffset + info->position, NULL, FILE_BEGIN); + pFile->Seek(info->entry->fileOffset + info->position); + + DWORD numWrite = 0; + numWrite = pFile->Read(buffer, length); + //ReadFile(pFile, buffer, length, &numWrite, NULL); + curFilePosition +=numWrite; + info->position += numWrite; + return numWrite; + } + int PakFile::fwrite(PakEntryHandle entry, const void* buffer, int length) + { + UNREFERENCED_PARAMETER(entry); + UNREFERENCED_PARAMETER(buffer); + UNREFERENCED_PARAMETER(length); + return 0; + } + void PakFile::fclose(PakEntryHandle entry) + { + UNREFERENCED_PARAMETER(entry); + } + int PakFile::fget_size(PakEntryHandle entry) + { + if(entry < 0 || entry >= PAK_MAX_OPEN_ENTRY) + throw "PAK entry is invalid! Can not seek"; + + return openEntries[entry].entry->fileSize; + } + void PakFile::fseek(PakEntryHandle entry, int position) + { + if(entry < 0 || entry >= PAK_MAX_OPEN_ENTRY) + throw "PAK entry is invalid! Can not seek"; + + if(openEntries[entry].entry->fileSize < position || position < 0) + throw "Invalid PAK position specified! Fseek()"; + + openEntries[entry].position = position; + } + int PakFile::ftell(PakEntryHandle entry) + { +#ifdef _DEBUG + if(entry < 0 || entry >= PAK_MAX_OPEN_ENTRY) + return -1; +#endif + return openEntries[entry].position; + } + void PakFile::NewFileEntry(const String& path) + { + if(!freeFileSlots) + { + hFiles = (FileEntry*)realloc(hFiles, sizeof(FileEntry) * header.filesCount + sizeof(FileEntry) * 16); + freeFileSlots = 16; + } + + //SetFilePointer(pFile, sizeof(PakHeader) + header.dataChunkLength, NULL, FILE_BEGIN); + pFile->Seek(sizeof(PakHeader) + header.dataChunkLength); + currentMode = PM_WRITE_ENTRY; + tmpEntry = hFiles + header.filesCount; + header.filesCount++; + + freeFileSlots--; + memset(tmpEntry,0,sizeof(FileEntry)); + path.copy(tmpEntry->name, path.length()); + tmpEntry->fileOffset = sizeof(PakHeader) + header.dataChunkLength; + } + + /*void PakFile::CloseCurrentEntry() + { + currentMode = PM_IDLE; + } + void PakFile::WriteCurrentEntry(const void* pData, int length) + { + DWORD numWrite = 0; + //WriteFile(pFile, pData, length, &numWrite, NULL); + numWrite = pFile->Write(pData, length); + header.dataChunkLength+=length; + tmpEntry->fileSize+=length; + curFilePosition+=numWrite; + }*/ + int PakFile::LocateEntry(const String& name) + { + int lastEntry = curEntryId; + for(int i = 0; i < header.filesCount; i++) + { + curEntryId = i; + LocateEntry(curEntryId); + if(tmpEntry->name == name) + return PAK_OK; + } + curEntryId = lastEntry; + LocateEntry(curEntryId); + return PAK_ARG_ERROR; + } + int PakFile::LocateEntry(int id) + { + if(id >= header.filesCount) + return PAK_ARG_ERROR; + curEntryId = id; + tmpEntry = hFiles + id; + return PAK_OK; + } + void PakFile::Close() + { + header.extraDataOffset = sizeof(PakHeader) + header.dataChunkLength; + DumpHeader(); + DumpExtraData(); + } + + void PakFile::DeleteFileEntry(const String& path) + { + if(LocateEntry(path) == PAK_OK) + { + if(IsLastEntry(tmpEntry)) + { + header.filesCount--; + header.dataChunkLength-= tmpEntry->fileSize; + memset(tmpEntry, 0, sizeof(FileEntry)); + } + else + { + header.filesCount--; + memmove(tmpEntry, tmpEntry + 1, (header.filesCount - curEntryId) * sizeof(FileEntry)); + } + } + } + BOOL PakFile::IsLastEntry(const FileEntry* entry) + { + return (UINT)(entry->fileOffset + entry->fileSize) == sizeof(PakHeader) + header.dataChunkLength; + } + /*void PakFile::OpenCurrentEntry() + { + curFilePosition = 0; + currentMode = PM_READ_ENTRY; + //SetFilePointer(pFile, tmpEntry->fileOffset, NULL, FILE_BEGIN); + pFile->Seek(tmpEntry->fileOffset); + } + int PakFile::ReadCurrentEntry(void *pBuffer, int length) + { + int maxCnt = tmpEntry->fileSize - curFilePosition; + if(!length || !maxCnt) + return 0; + if(length > maxCnt) + length = maxCnt; + DWORD numWrite = 0; + //ReadFile(pFile, pBuffer, length, &numWrite, NULL); + numWrite = pFile->Read(pBuffer, length); + + curFilePosition +=numWrite; + return numWrite; + }*/ + /*void PakFile::SeekCurrentEntry(int position) + { + SetFilePointer(pFile, tmpEntry->fileOffset + position, NULL, FILE_BEGIN); + pFile->Seek(tmpEntry->fileOffset + position); + curFilePosition = position; + } + int PakFile::TellCurrentEntry() + { + return curFilePosition; + }*/ + int PakFile::filesCount() + { + return header.filesCount; + } + entry_info PakFile::GetEntryInfo(int id) + { + if(id < 0 || id >= filesCount()) + throw "PAK entry is invalid! Can not get info"; + FileEntry* entry = hFiles + id; + entry_info info; + strcpy_s(info.fileName, entry->name); + info.fileSize = entry->fileSize; + return info; + } +} \ No newline at end of file diff --git a/LifeCore/vfs/pak/pak_io/PakFile.h b/LifeCore/vfs/pak/pak_io/PakFile.h new file mode 100644 index 0000000..4a88a20 --- /dev/null +++ b/LifeCore/vfs/pak/pak_io/PakFile.h @@ -0,0 +1,80 @@ +#ifndef PAK_FILE_H__ +#define PAK_FILE_H__ + +#include +#include +#include "../../../LString.h" +#include "PakTypes.h" +#include +#include "pakHeader.h" +#include "FolderEntry.h" +#include "FileEntry.h" +#include "EntryInfo.h" +#include "../../../IDataStream.h" +#include +#define PAK_VERSION 0x0100 +#define PAK_MAX_OPEN_ENTRY 256 + +namespace LifeCore +{ + struct OpenEntryInfo + { + FileEntry* entry; + int position; + }; + typedef int PakEntryHandle; + + class PakFile + { + enum PAK_MODE + { + PM_WRITE_ENTRY = 0, + PM_READ_ENTRY = 1, + PM_EDIT_ENTRY = 2, + PM_IDLE = 3 + }; + + public: + PakFile(void); + ~PakFile(void); + int Open(IDataStream* source); + + void Close(); + int filesCount(); + entry_info GetEntryInfo(int id); + PakEntryHandle fopen(const String& name); + int fread(PakEntryHandle entry, void* buffer, int length); + int fwrite(PakEntryHandle entry, const void* buffer, int length); + int ftell(PakEntryHandle entry); + void fseek(PakEntryHandle entry, int position); + void fclose(PakEntryHandle entry); + int fget_size(PakEntryHandle entry); + private: + int curFilePosition; + int curEntryId; + int freeFileSlots; + int freeFolderSlots; + int currentMode; + FolderEntry* hFolders; + FileEntry* hFiles; + IDataStream* pFile; + PakHeader header; + void DumpExtraData(); + void DumpHeader(); + FileEntry* tmpEntry; + + void NewFolderEntry(const String& path); + void NewFileEntry(const String& path); + void DeleteFileEntry(const String& path); + + int LocateEntry(const String& name); + int LocateEntry(int id); + + OpenEntryInfo openEntries[PAK_MAX_OPEN_ENTRY]; + int openEntriesCount; + + BOOL IsLastEntry(const FileEntry* entry); + }; +} + +#endif \ No newline at end of file diff --git a/LifeCore/vfs/pak/pak_io/PakTypes.h b/LifeCore/vfs/pak/pak_io/PakTypes.h new file mode 100644 index 0000000..fce3b48 --- /dev/null +++ b/LifeCore/vfs/pak/pak_io/PakTypes.h @@ -0,0 +1,16 @@ +#ifndef PAK_TYPES_H__ +#define PAK_TYPES_H__ + +#define PAK_ARG_ERROR (-100) +#define PAK_INTERNAL_ERROR (-101) +#define PAK_BAD_FILE (-102) +#define PAK_OK (0) +#include "../../../LString.h" + +#ifdef WIN32 +#define PATH_SPLITTER _text("\\") +#else +#define PATH_SPLITTER _text("/") +#endif + +#endif \ No newline at end of file diff --git a/LifeCore/vfs/pak/pak_io/pakHeader.h b/LifeCore/vfs/pak/pak_io/pakHeader.h new file mode 100644 index 0000000..6085605 --- /dev/null +++ b/LifeCore/vfs/pak/pak_io/pakHeader.h @@ -0,0 +1,12 @@ +#include + +typedef struct PakHeader +{ + int version; + int whiteSpacesCount; + int filesCount; + int foldersCount; + int extraDataOffset; + int lastFolderId; + int dataChunkLength; +} PakHeader; \ No newline at end of file diff --git a/LifeCore/vfs/pak/pak_io/targetver.h b/LifeCore/vfs/pak/pak_io/targetver.h new file mode 100644 index 0000000..082ce85 --- /dev/null +++ b/LifeCore/vfs/pak/pak_io/targetver.h @@ -0,0 +1,12 @@ +#pragma once + +// The following macros define the minimum required platform. The minimum required platform +// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run +// your application. The macros work by enabling all features available on platform versions up to and +// including the version specified. + +// Modify the following defines if you have to target a platform prior to the ones specified below. +// Refer to MSDN for the latest info on corresponding values for different platforms. +#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista. +#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows. +#endif \ No newline at end of file diff --git a/LifeCore/vfs/zip/IZipFile.cpp b/LifeCore/vfs/zip/IZipFile.cpp new file mode 100644 index 0000000..5e22df8 --- /dev/null +++ b/LifeCore/vfs/zip/IZipFile.cpp @@ -0,0 +1,41 @@ +#include "IZipFile.h" +#include "IZipFileContentProvider.h" + +namespace LifeCore +{ + IZipFile::IZipFile() + { + isOpen = false; + } + IDataStreamPtr IZipFile::OpenWrite(int mode) + { + UNREFERENCED_PARAMETER(mode); + g_stream = new IZipStream(((IZipFileContentProvider*)this->Provider)->g_sourceFile, EntryName, FA_WRITE, ((IZipFileContentProvider*)this->Provider)->g_rootFolder); + return IDataStreamPtr(g_stream); + } + IDataStreamPtr IZipFile::OpenReadWrite(int mode) + { + UNREFERENCED_PARAMETER(mode); + throw; + //return IDataStreamPtr(0); + //throw new NotImplementedException(); + } + + IDataStreamPtr IZipFile::OpenRead() + { + IZipFileContentProvider* prov = (IZipFileContentProvider*)this->Provider; + prov->g_sourceFile = unzOpen(prov->g_rootFolder.c_str()); + g_stream = new IZipStream(prov->g_sourceFile, EntryName, FA_READ, NULL); + return IDataStreamPtr(g_stream); + //g_stream = new IZipStream((this->Provider as IZipFileContentProvider).GetSource(), EntryName, FA_READ); + //return g_stream; + } + void IZipFile::Close() + { + if(g_stream) + { + g_stream->Close(); + delete g_stream; + } + } +} \ No newline at end of file diff --git a/LifeCore/vfs/zip/IZipFile.h b/LifeCore/vfs/zip/IZipFile.h new file mode 100644 index 0000000..b71f517 --- /dev/null +++ b/LifeCore/vfs/zip/IZipFile.h @@ -0,0 +1,27 @@ +#ifndef IZIPFILE_H__ +#define IZIPFILE_H__ + +//#include "IDataStream.h" +#include "../../LString.h" +#include "../IFile.h" +#include "IZipStream.h" + +namespace LifeCore +{ + class IZipFile : public IFile + { + public: + IZipFile(); + ~IZipFile(){} + String EntryName; + IDataStreamPtr OpenWrite(int mode); + IDataStreamPtr OpenReadWrite(int mode); + IDataStreamPtr OpenRead(); + void Close(); + private: + int offsetIndex; + IZipStream* g_stream; + }; +} + +#endif \ No newline at end of file diff --git a/LifeCore/vfs/zip/IZipFileContentProvider.cpp b/LifeCore/vfs/zip/IZipFileContentProvider.cpp new file mode 100644 index 0000000..bd907c3 --- /dev/null +++ b/LifeCore/vfs/zip/IZipFileContentProvider.cpp @@ -0,0 +1,83 @@ +#include "IZipFileContentProvider.h" +namespace LifeCore +{ + static const String zipPathSplitter(_text("/")); + + IZipFileContentProvider::IZipFileContentProvider(const String& zipFile) + { + IZipFolder* folder = new IZipFolder(); + g_rootFolder = zipFile; + folder->Provider = this; + folder->Name = GetFileNameWithoutExtention(zipFile); + + folder->EntryName = _text(""); + + g_fs = new IFileSystem(folder); + + g_sourceFile = unzOpen(zipFile.c_str()); + + unzGetGlobalInfo(g_sourceFile,&globalInfo); + g_fs->GetRootFolder(); + BuildTree(); + unzClose(g_sourceFile); + } + + void IZipFileContentProvider::BuildTree() + { + unzGoToFirstFile(g_sourceFile); + + for (unsigned int i = 0; i < globalInfo.number_entry; i++) + { + tchar fileName[256+1] = {0}; + unz_file_info fileInfo; + + unzGetCurrentFileInfo(g_sourceFile,&fileInfo,fileName,256,NULL,0,NULL, 0); + + if (fileInfo.size_filename >0 && fileName[fileInfo.size_filename-1] == '/') + { + /* string path = entry.Name; + path = path.Replace('/', '\\'); + IZipFolder folder = BuildDirectory(path);*/ + } + else//is file + { + String sd = String(fileName); + String p = GetZipPathWithoutFilename(sd); + IZipFolder* folder; + g_fs->GetFolder(p, (IFolder**)&folder); + + IZipFile* file = new IZipFile(); + file->FileName = GetFileName(sd, zipPathSplitter); + file->Provider = this; + file->Size = fileInfo.uncompressed_size; + file->EntryName = sd; + folder->AddFile(file); + } + unzCloseCurrentFile(g_sourceFile); + unzGoToNextFile(g_sourceFile); + } + + //print folder tree + String tab(_text("\t")); + int cnt = g_fs->GetRootFolder()->folderCount; + for(int i = 0; i < cnt; i++) + { + IFolder* fld = g_fs->GetRootFolder()->GetFolder(i); + PRINT(_text("%s\n"), fld->Name.c_str()); + PRINT(_text("Files count: %i\n"), fld->fileCount); + } + } + + String IZipFileContentProvider::GetZipPathWithoutFilename(const String& path) + { + int offset = path.find_last_of(_text("/")); + if(offset>=0) + { + return path.substr(0, offset); + } + else + { + return _text("/"); + } + } +} \ No newline at end of file diff --git a/LifeCore/vfs/zip/IZipFileContentProvider.h b/LifeCore/vfs/zip/IZipFileContentProvider.h new file mode 100644 index 0000000..a68795b --- /dev/null +++ b/LifeCore/vfs/zip/IZipFileContentProvider.h @@ -0,0 +1,130 @@ +#ifndef IZIPFILECONTENTPROVIDER_H__ +#define IZIPFILECONTENTPROVIDER_H__ + +#include "../IContentProvider.h" +#include "../../LString.h" +#include "../IFileSystem.h" +//#include "test.h" +#include "../../minizip/unzip.h" +#include "IZipFolder.h" + +namespace LifeCore +{ + class IZipFileContentProvider : public IContentProvider + { + public: + ~IZipFileContentProvider(){}; + void Close(){} + IFileSystem* getFileSystem(){return g_fs;} + IZipFileContentProvider(const String& zipFile); + String g_rootFolder; + + protected: + unz_global_info globalInfo; + + IFileSystem* g_fs; + void BuildTree(); + String GetZipPathWithoutFilename(const String& path); + public: + unzFile g_sourceFile; + + /*private: + + private string GetFilename(string path) + { + return System.IO.Path.GetFileName("C:\\" + path); + } + + private IZipFolder BuildDirectory(string path) + { + string[] parts = path.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); + + IZipFolder folder = (IZipFolder)g_fs.GetRootFolder(); + string npath = ""; + for (int i = 0; i < parts.Length; i++) + { + npath+=parts[i] + "/"; + IZipFolder f= (IZipFolder)folder.GetFolder(parts[i]); + if (f != null) + { + folder = f; + } + else + { + f = new IZipFolder(); + f.EntryName = npath; + f.Name = parts[i]; + f.Provider = this; + folder.AddFolder(f); + folder = f; + } + } + return folder; + } + private int ScanFolder(IFolder f) + { + string P = f.Path; + string[] dirs = System.IO.Directory.GetDirectories(g_rootFolder + P); + for (int i = 0; i < dirs.Length; i++) + { + } + + string[] files = System.IO.Directory.GetFiles(g_rootFolder + P); + for (int i = 0; i < files.Length; i++) + { + IFSFile desc = new IFSFile(); + desc.FileName = System.IO.Path.GetFileName(files[i]); + desc.Provider = this; + FileInfo info = new FileInfo(files[i]); + desc.Size = (int)info.Length; + desc.FSPath = files[i]; + f.AddFile(desc); + } + + return dirs.Length; + } + + private string GetDirFromPath(string path) + { + string[] dirs = path.Split(System.IO.Path.DirectorySeparatorChar); + return dirs[dirs.Length - 1]; + } + + public: + ZipFile GetSource() + { + return g_sourceFile; + } + + IFolder CreateFolder(string path) + { + return null; + } + + IFileDescriptor CreateFile(string path) + { + System.IO.File.Create(path).Close(); + IFSFile file = new IFSFile(); + file.FileName = System.IO.Path.GetFileName(path); + file.FSPath = path; + file.Provider = this; + return file; + } + + override void Close() + { + if (g_dataStream != null) + g_dataStream.Close(); + } + + override LifeEngine.FileSystem.IFileSystem FileSystem + { + get + { + return g_fs; + } + }*/ + }; +} + +#endif \ No newline at end of file diff --git a/LifeCore/vfs/zip/IZipFolder.cpp b/LifeCore/vfs/zip/IZipFolder.cpp new file mode 100644 index 0000000..72f2b95 --- /dev/null +++ b/LifeCore/vfs/zip/IZipFolder.cpp @@ -0,0 +1,31 @@ +#include "IZipFolder.h" + +namespace LifeCore +{ + IZipFolder::~IZipFolder(void) + { + for( FolderMap::iterator it = g_folderCollection.begin(); it != g_folderCollection.end(); it++ ) + delete it->second; + } + IFolder* IZipFolder::NewFolder(const String& name) + { + IZipFolder* newFolder = new IZipFolder(); + newFolder->Name = name; + newFolder->setParent(this); + newFolder->EntryName = this->EntryName + name + _text("/"); + newFolder->Provider = this->Provider; + this->AddFolder(newFolder); + return newFolder; + } + IFile* IZipFolder::NewFile(const String& name, int size) + { + UNREFERENCED_PARAMETER(size); + IFile* newFile = new IZipFile(); + newFile->FileName = name; + newFile->Provider = this->Provider; + IZipFile* xf = (IZipFile*)newFile; + xf->EntryName = this->EntryName + name; + this->AddFile(newFile); + return newFile; + } +} \ No newline at end of file diff --git a/LifeCore/vfs/zip/IZipFolder.h b/LifeCore/vfs/zip/IZipFolder.h new file mode 100644 index 0000000..5aa5e53 --- /dev/null +++ b/LifeCore/vfs/zip/IZipFolder.h @@ -0,0 +1,25 @@ +#ifndef IZIPFOLDER_H__ +#define IZIPFOLDER_H__ + +#include "../../IDataStream.h" +#include "../../LString.h" +#include "../IFile.h" +#include "../IFolder.h" +#include "IZipFile.h" +#include "../IContentProvider.h" +#include + +namespace LifeCore +{ + class IZipFolder : public IFolder + { + public: + ~IZipFolder(void); + IContentProvider* Provider; + String EntryName; + IFolder* NewFolder(const String& name); + IFile* NewFile(const String& name, int size); + }; +} + +#endif \ No newline at end of file diff --git a/LifeCore/vfs/zip/IZipStream.h b/LifeCore/vfs/zip/IZipStream.h new file mode 100644 index 0000000..fd695cb --- /dev/null +++ b/LifeCore/vfs/zip/IZipStream.h @@ -0,0 +1,108 @@ +#ifndef IZIPSTREAM_H__ +#define IZIPSTREAM_H__ + +#include "../../IDataStream.h" +#include "../../LString.h" +#include +#include "../../minizip/unzip.h" +#include "../../minizip/zip.h" + +namespace LifeCore +{ + class IZipStream : public IDataStream + { + private: + size_t stmLength; + unzFile source; + zipFile dest; + zip_fileinfo destInfo; + unz_file_info fileInfo; + int openMode; + public: + ~IZipStream(){} + void Close() + { + if(openMode == FA_READ) + { + if(source) + { + unzCloseCurrentFile(source); + unzClose(source); + } + } + else //write + { + if(dest) + { + zipCloseFileInZip(dest); + zipClose(dest, "ONOTOLE NEGODUE"); + } + } + openMode = 0; + } + IZipStream(unzFile& file, String& entry, int fileAccessMode, const String& archivePath) + { + source = file; + openMode = fileAccessMode; + if (fileAccessMode == FA_READ) + { + tchar fileName[256] = {0}; + unzLocateFile(file, entry.c_str(), 1); + unzGetCurrentFileInfo(file,&fileInfo,fileName,256,NULL,0,NULL, 0); + stmLength = fileInfo.uncompressed_size; + unzOpenCurrentFile(file); + } + else + { + dest = zipOpen(archivePath.c_str(), 2); + destInfo.tmz_date.tm_sec = destInfo.tmz_date.tm_min = destInfo.tmz_date.tm_hour = + destInfo.tmz_date.tm_mday = destInfo.tmz_date.tm_mon = destInfo.tmz_date.tm_year = 0; + destInfo.dosDate = 0; + destInfo.internal_fa = 0; + destInfo.external_fa = 0; + +#ifdef _UNICODE + char tmp[64] = {0}; + wcstombs(tmp,entry.c_str(),entry.length()); + zipOpenNewFileInZip(dest, tmp,&destInfo,NULL,0,NULL, 0, NULL, 0, 0); +#else + zipOpenNewFileInZip(dest, entry.c_str(),&destInfo,NULL,0,NULL, 0, NULL, 0, 0); +#endif + } + //g_openMode = mode; + } + size_t Read(void* pBuffer, size_t countBytes) + { + return unzReadCurrentFile(source, pBuffer, countBytes); + } + size_t Write(const void* pBuffer, size_t countBytes) + { + zipWriteInFileInZip(dest, pBuffer,countBytes); + return countBytes; + } + void Seek(size_t position) + { + printf("100500&%i\n", position); + printf("Unimplemented SEEK operation in IZipStream"); + } + size_t Tell() + { + printf("Unimplemented TELL operation in IZipStream"); + return 0; + } + size_t Length() + { + return stmLength; + } + bool CanRead() + { + return true; + } + bool CanWrite() + { + return true; + } + }; +} + +#endif \ No newline at end of file diff --git a/LifeExport/Exports.def b/LifeExport/Exports.def new file mode 100644 index 0000000..763a106 --- /dev/null +++ b/LifeExport/Exports.def @@ -0,0 +1,9 @@ +LIBRARY +EXPORTS + LibDescription @1 PRIVATE + LibNumberClasses @2 PRIVATE + LibClassDesc @3 PRIVATE + LibVersion @4 PRIVATE + CanAutoDefer @5 PRIVATE + SECTIONS + .data READ WRITE \ No newline at end of file diff --git a/LifeExport/LifeExport.cpp b/LifeExport/LifeExport.cpp new file mode 100644 index 0000000..8af91ac --- /dev/null +++ b/LifeExport/LifeExport.cpp @@ -0,0 +1,163 @@ +// LifeExport.cpp : Defines the exported functions for the DLL application. +// + +#include "stdafx.h" +#include "LifeExport.h" +#include "IGame/IGame.h" +#include "IGame/IGameObject.h" +#include +//#include "IGame/IGameFX.h" + +#include "3dsmaxport.h" +#include +#include + +#define MYEXP_CLASS_ID Class_ID(0x6b1850ce, 0x77e51de8) + +HMODULE hMod; + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + hMod = hModule; + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + +class LifeExport : public SceneExport +{ +public: + int ExtCount() + { + return 1; + } + const MCHAR * Ext(int n) + { + return _TEXT("vmf"); + } + const MCHAR * LongDesc() + { + return _TEXT("LifeEngine export plugin"); + } + const MCHAR * ShortDesc() + { + return _TEXT("LifeEngine export"); + } + const MCHAR * AuthorName() + { + return _TEXT("Sergei Serb (L)"); + } + const MCHAR * CopyrightMessage() + { + return _TEXT("Massive Dynamics (L)"); + } + const MCHAR * OtherMessage1() + { + return _TEXT("trololo"); + } + const MCHAR * OtherMessage2() + { + return _TEXT("trololo"); + } + unsigned int Version() + { + return 1; + } + void ShowAbout(HWND hWnd) + { + } + int DoExport(const MCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts=FALSE, DWORD options=0) + { + typedef int (*ExportFunc)(const MCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts, DWORD options); + + wchar_t dir[512]; + GetModuleFileNameW(hMod, dir, sizeof(dir)); + + std::wstring srcPath(dir); + int pos = srcPath.find_last_of(_TEXT("/\\")); + + std::wstring directory = srcPath.substr(0, pos); + + directory += _TEXT("\\LifeExportInternal.dll"); + + HMODULE lib = LoadLibrary(directory.c_str()); + std::string result = lib == 0 ? "Failed to load export DLL!" : "Succeeded to load export DLL!"; + + FILE* f = fopen("C:\\#export_root.txt", "wb"); + char buf[512]; + sprintf(buf, "\n\nResult: %s\n", result.c_str()); + //fwrite(directory.c_str(), 1,directory.length(), f); + fwrite(buf, 1,strlen(buf), f); + + if(lib) + { + ExportFunc func = (ExportFunc)GetProcAddress(lib, "DoExport"); + + sprintf(buf, "Try to get export func: %i\n", (func != 0)); + fwrite(buf, 1,strlen(buf), f); + + if(func != 0) + { + func(name, ei, i, suppressPrompts, options); + } + + FreeLibrary(lib); + } + + fclose(f); + + return TRUE; + } +}; + +class MyPlugClassDesc : public ClassDesc +{ +public: + int IsPublic() { return 1; } + void* Create(BOOL Loading = FALSE) { return new LifeExport(); } + const TCHAR * ClassName() { return _T("LifeExport"); } + SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; } + Class_ID ClassID() { return MYEXP_CLASS_ID; } + const TCHAR * Category() { return _T(""); } +}; + +static MyPlugClassDesc desc; + +LIFEEXPORT_API const TCHAR* LibDescription() +{ + return _TEXT("LifeEngine export plugin"); +} + +//TODO: Must change this number when adding a new class +LIFEEXPORT_API int LibNumberClasses() +{ + return 1; +} + +LIFEEXPORT_API ClassDesc* LibClassDesc(int i) +{ + switch(i) { + case 0: return new MyPlugClassDesc(); + default: return 0; + } +} + +LIFEEXPORT_API ULONG LibVersion() +{ + return VERSION_3DSMAX; +} + +// Let the plug-in register itself for deferred loading +LIFEEXPORT_API ULONG CanAutoDefer() +{ + return 1; +} \ No newline at end of file diff --git a/LifeExport/LifeExport.h b/LifeExport/LifeExport.h new file mode 100644 index 0000000..ca43ed9 --- /dev/null +++ b/LifeExport/LifeExport.h @@ -0,0 +1,6 @@ +// defined with this macro as being exported. +#ifdef LIFEEXPORT_EXPORTS +#define LIFEEXPORT_API __declspec(dllexport) +#else +#define LIFEEXPORT_API __declspec(dllimport) +#endif \ No newline at end of file diff --git a/LifeExport/LifeExport.vcxproj b/LifeExport/LifeExport.vcxproj new file mode 100644 index 0000000..e7bfa4c --- /dev/null +++ b/LifeExport/LifeExport.vcxproj @@ -0,0 +1,197 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {83915350-90FC-4826-B91F-2AEA0574BFE8} + Win32Proj + LifeExport + $(VCTargetsPath11) + <Project Location In Database> + <Source Control Database> + <Local Binding Root of Project> + Mercurial Source Control Package + + + + DynamicLibrary + true + Unicode + v120 + + + DynamicLibrary + true + Unicode + v120 + + + DynamicLibrary + false + true + Unicode + v120 + + + DynamicLibrary + false + true + Unicode + v120 + + + + + + + + + + + + + + + + + + + true + .dle + + + true + .dle + + + false + .dle + + + false + .dle + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;LIFEEXPORT_EXPORTS;%(PreprocessorDefinitions) + C:\Program Files\Autodesk\3ds Max 2014 SDK\maxsdk\include + MultiThreadedDLL + + + Windows + true + $(ADSK_3DSMAX_SDK_2014)lib\x64\Release + core.lib;geom.lib;gfx.lib;mesh.lib;maxutil.lib;maxscrpt.lib;paramblk2.lib;msxml2.lib;igame.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + Exports.def + + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;LIFEEXPORT_EXPORTS;%(PreprocessorDefinitions) + C:\Program Files\Autodesk\3ds Max 2014 SDK\maxsdk\include + MultiThreadedDLL + + + Windows + true + C:\Program Files\Autodesk\3ds Max 2014 SDK\maxsdk\lib\x64\Release + core.lib;geom.lib;gfx.lib;mesh.lib;maxutil.lib;maxscrpt.lib;paramblk2.lib;msxml2.lib;igame.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + Exports.def + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;LIFEEXPORT_EXPORTS;%(PreprocessorDefinitions) + C:\Program Files\Autodesk\3ds Max 2014 SDK\maxsdk\include + + + Windows + true + true + true + Exports.def + $(ADSK_3DSMAX_SDK_2014)lib\x64\Release + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;LIFEEXPORT_EXPORTS;%(PreprocessorDefinitions) + C:\Program Files\Autodesk\3ds Max 2014 SDK\maxsdk\include + + + Windows + true + true + true + Exports.def + C:\Program Files\Autodesk\3ds Max 2014 SDK\maxsdk\lib\x64\Release + core.lib;geom.lib;gfx.lib;mesh.lib;maxutil.lib;maxscrpt.lib;paramblk2.lib;msxml2.lib;igame.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + false + false + + + + + false + false + + + + + + + + Create + Create + Create + Create + + + + + + \ No newline at end of file diff --git a/LifeExport/dllmain.cpp b/LifeExport/dllmain.cpp new file mode 100644 index 0000000..e69de29 diff --git a/LifeExport/stdafx.cpp b/LifeExport/stdafx.cpp new file mode 100644 index 0000000..181b615 --- /dev/null +++ b/LifeExport/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// LifeExport.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file \ No newline at end of file diff --git a/LifeExport/stdafx.h b/LifeExport/stdafx.h new file mode 100644 index 0000000..5eaa9fb --- /dev/null +++ b/LifeExport/stdafx.h @@ -0,0 +1,14 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include + +// TODO: reference additional headers your program requires here \ No newline at end of file diff --git a/LifeExport/targetver.h b/LifeExport/targetver.h new file mode 100644 index 0000000..5b1f29c --- /dev/null +++ b/LifeExport/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include \ No newline at end of file diff --git a/LifeExportInternal/Exports.def b/LifeExportInternal/Exports.def new file mode 100644 index 0000000..f0e08d3 --- /dev/null +++ b/LifeExportInternal/Exports.def @@ -0,0 +1,5 @@ +LIBRARY LifeExportInternal +EXPORTS + DoExport @1 PRIVATE +SECTIONS + .data READ WRITE \ No newline at end of file diff --git a/LifeExportInternal/LifeExportInternal.cpp b/LifeExportInternal/LifeExportInternal.cpp new file mode 100644 index 0000000..717e045 --- /dev/null +++ b/LifeExportInternal/LifeExportInternal.cpp @@ -0,0 +1,81 @@ +// LifeExportInternal.cpp : Defines the exported functions for the DLL application. +// + +#include "stdafx.h" +#include "LifeExportInternal.h" +#include "IGame/IGame.h" +#include "IGame/IGameObject.h" +#include +//#include "IGame/IGameFX.h" + +#include "3dsmaxport.h" +#include + +struct Vertex +{ + Point3 Position; + Point3 Normal; + Point3 Tangent; + Point3 Binormal; + Point2 Texcoord1; + Point2 Texcoord2; +}; + +LIFEEXPORTINTERNAL_API int DoExport(const MCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts=FALSE, DWORD options=0) +{ + IGameScene * pIgame = GetIGameInterface(); + + IGameConversionManager * cm = GetConversionManager(); + cm->SetCoordSystem(IGameConversionManager::IGAME_D3D); + bool exportSelected = (options & SCENE_EXPORT_SELECTED) ? true : false; + + pIgame->InitialiseIGame(exportSelected); + pIgame->SetStaticFrame(0); + + FILE* f = fopen("C:\\#export_internal.txt", "wb"); + + int nodeCount = pIgame->GetTopLevelNodeCount(); + + for(int i = 0; i < nodeCount; i++) + { + IGameNode* node = pIgame->GetTopLevelNode(i); + + if(node->IsTarget()) + continue; + + IGameObject* obj = node->GetIGameObject(); + + switch(obj->GetIGameType()) + { + case IGameObject::IGAME_MESH: + { + IGameMesh* mesh = (IGameMesh*)obj; + + mesh->SetCreateOptimizedNormalList(); + if(mesh->InitializeData()) + { + int faceCount = mesh->GetNumberOfFaces(); + + //mesh->GetNormal() + + std::wstring nodeName(node->GetName()); + char nameSrc[512]; + wcstombs(nameSrc, nodeName.c_str(), 512); + nameSrc[nodeName.length()] = 0; + std::string realName(nameSrc); + + sprintf(nameSrc, "NodeName: %s, FaceCount: %i\n", realName.c_str(), faceCount); + fwrite(nameSrc, 1,strlen(nameSrc), f); + } + } + break; + } + } + + char buf[128]; + sprintf(buf, "Objects count: %i", nodeCount); + + fwrite(buf, 1,strlen(buf), f); + fclose(f); + return 1; +} \ No newline at end of file diff --git a/LifeExportInternal/LifeExportInternal.h b/LifeExportInternal/LifeExportInternal.h new file mode 100644 index 0000000..27b07ad --- /dev/null +++ b/LifeExportInternal/LifeExportInternal.h @@ -0,0 +1,11 @@ +// The following ifdef block is the standard way of creating macros which make exporting +// from a DLL simpler. All files within this DLL are compiled with the LIFEEXPORTINTERNAL_EXPORTS +// symbol defined on the command line. This symbol should not be defined on any project +// that uses this DLL. This way any other project whose source files include this file see +// LIFEEXPORTINTERNAL_API functions as being imported from a DLL, whereas this DLL sees symbols +// defined with this macro as being exported. +#ifdef LIFEEXPORTINTERNAL_EXPORTS +#define LIFEEXPORTINTERNAL_API __declspec(dllexport) +#else +#define LIFEEXPORTINTERNAL_API __declspec(dllimport) +#endif \ No newline at end of file diff --git a/LifeExportInternal/LifeExportInternal.vcxproj b/LifeExportInternal/LifeExportInternal.vcxproj new file mode 100644 index 0000000..51945e5 --- /dev/null +++ b/LifeExportInternal/LifeExportInternal.vcxproj @@ -0,0 +1,190 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {E9BBB863-0237-4F30-B8AD-EFED25A3A61B} + Win32Proj + LifeExportInternal + $(VCTargetsPath11) + <Project Location In Database> + <Source Control Database> + <Local Binding Root of Project> + Mercurial Source Control Package + + + + DynamicLibrary + true + Unicode + v120 + + + DynamicLibrary + true + Unicode + v120 + + + DynamicLibrary + false + true + Unicode + v120 + + + DynamicLibrary + false + true + Unicode + v120 + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;LIFEEXPORTINTERNAL_EXPORTS;%(PreprocessorDefinitions) + C:\Program Files (x86)\Autodesk\maxsdk\include + + + Windows + true + Exports.def + + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;LIFEEXPORTINTERNAL_EXPORTS;%(PreprocessorDefinitions) + C:\Program Files\Autodesk\3ds Max 2014 SDK\maxsdk\include;$(SolutionDir)\LifeCore\include + + + Windows + true + Exports.def + C:\Program Files\Autodesk\3ds Max 2014 SDK\maxsdk\lib\x64\Release;$(SolutionDir)\LifeCore\lib + LifeCore_d.lib;core.lib;geom.lib;gfx.lib;mesh.lib;maxutil.lib;maxscrpt.lib;paramblk2.lib;msxml2.lib;igame.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + copy "$(OutDir)$(TargetName)$(TargetExt)" "$(ADSK_3DSMAX_x64_2013)plugins\$(TargetName)$(TargetExt)" + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;LIFEEXPORTINTERNAL_EXPORTS;%(PreprocessorDefinitions) + + + Windows + true + true + true + Exports.def + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;LIFEEXPORTINTERNAL_EXPORTS;%(PreprocessorDefinitions) + C:\Program Files\Autodesk\3ds Max 2014 SDK\maxsdk\include + + + Windows + true + true + true + Exports.def + LifeCore_d.lib;core.lib;geom.lib;gfx.lib;mesh.lib;maxutil.lib;maxscrpt.lib;paramblk2.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + false + false + + + + + false + false + + + + + + + + Create + Create + Create + Create + + + + + + \ No newline at end of file diff --git a/LifeExportInternal/ReadMe.txt b/LifeExportInternal/ReadMe.txt new file mode 100644 index 0000000..bdbea95 --- /dev/null +++ b/LifeExportInternal/ReadMe.txt @@ -0,0 +1,40 @@ +======================================================================== + DYNAMIC LINK LIBRARY : LifeExportInternal Project Overview +======================================================================== + +AppWizard has created this LifeExportInternal DLL for you. + +This file contains a summary of what you will find in each of the files that +make up your LifeExportInternal application. + + +LifeExportInternal.vcxproj + This is the main project file for VC++ projects generated using an Application Wizard. + It contains information about the version of Visual C++ that generated the file, and + information about the platforms, configurations, and project features selected with the + Application Wizard. + +LifeExportInternal.vcxproj.filters + This is the filters file for VC++ projects generated using an Application Wizard. + It contains information about the association between the files in your project + and the filters. This association is used in the IDE to show grouping of files with + similar extensions under a specific node (for e.g. ".cpp" files are associated with the + "Source Files" filter). + +LifeExportInternal.cpp + This is the main DLL source file. + +///////////////////////////////////////////////////////////////////////////// +Other standard files: + +StdAfx.h, StdAfx.cpp + These files are used to build a precompiled header (PCH) file + named LifeExportInternal.pch and a precompiled types file named StdAfx.obj. + +///////////////////////////////////////////////////////////////////////////// +Other notes: + +AppWizard uses "TODO:" comments to indicate parts of the source code you +should add to or customize. + +///////////////////////////////////////////////////////////////////////////// diff --git a/LifeExportInternal/dllmain.cpp b/LifeExportInternal/dllmain.cpp new file mode 100644 index 0000000..503cf63 --- /dev/null +++ b/LifeExportInternal/dllmain.cpp @@ -0,0 +1,18 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "stdafx.h" + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} \ No newline at end of file diff --git a/LifeExportInternal/stdafx.cpp b/LifeExportInternal/stdafx.cpp new file mode 100644 index 0000000..1f87a6c --- /dev/null +++ b/LifeExportInternal/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// LifeExportInternal.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file \ No newline at end of file diff --git a/LifeExportInternal/stdafx.h b/LifeExportInternal/stdafx.h new file mode 100644 index 0000000..5eaa9fb --- /dev/null +++ b/LifeExportInternal/stdafx.h @@ -0,0 +1,14 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include + +// TODO: reference additional headers your program requires here \ No newline at end of file diff --git a/LifeExportInternal/targetver.h b/LifeExportInternal/targetver.h new file mode 100644 index 0000000..5b1f29c --- /dev/null +++ b/LifeExportInternal/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include \ No newline at end of file diff --git a/LifeGUI/LifeGUI.vcxproj b/LifeGUI/LifeGUI.vcxproj new file mode 100644 index 0000000..1eb09c2 --- /dev/null +++ b/LifeGUI/LifeGUI.vcxproj @@ -0,0 +1,251 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {3F1B6A31-4B40-4A4D-A213-35AAE300BA1C} + Win32Proj + LifeGUI + + + + StaticLibrary + true + v120 + MultiByte + + + StaticLibrary + true + v120 + MultiByte + + + StaticLibrary + false + v120 + true + MultiByte + + + StaticLibrary + false + v120 + true + MultiByte + + + + + + + + + + + + + + + + + + + $(ProjectName)_d + $(SolutionDir)$(ProjectName)\lib\ + $(SolutionDir)$(ProjectName)\tmp\$(Configuration)\ + + + $(ProjectName)_d + + + $(SolutionDir)$(ProjectName)\lib\ + $(SolutionDir)$(ProjectName)\tmp\$(Configuration)\ + + + + + + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + $(SolutionDir)LifeCore\;$(REPO_DIR)\freetype-2.4.3\include + true + false + + + Windows + true + + + freetype243_D.lib; + $(REPO_DIR)\freetype-2.4.3\objs\win32\vc2008 + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + $(SolutionDir)LifeCore\include;$(ProjectDir)include;$(REPO_DIR)\freetype-2.4.3\include + true + false + + + Windows + true + + + freetype243_D.lib; + $(REPO_DIR)\freetype-2.4.3\objs\win32\vc2008 + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + $(SolutionDir)LifeCore\;$(REPO_DIR)\freetype-2.4.3\include + + + Windows + true + true + true + + + $(REPO_DIR)\freetype-2.4.3\objs\win32\vc2008 + freetype243.lib; + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + + + Windows + true + true + true + + + + + + \ No newline at end of file diff --git a/LifeGUI/src/GUIBorder.h b/LifeGUI/src/GUIBorder.h new file mode 100644 index 0000000..87f6321 --- /dev/null +++ b/LifeGUI/src/GUIBorder.h @@ -0,0 +1,50 @@ +#ifndef GUIBorder_h__ +#define GUIBorder_h__ + +#include + +namespace LifeGUI +{ + struct GUIBorder + { + int32 Top, Bottom, Left, Right; + GUIBorder() + { + Top = Bottom = Left = Right = 0; + } + GUIBorder(int32 size) + { + Top = Bottom = Left = Right = size; + } + GUIBorder(int32 h_size, int32 v_size) + { + Top = Bottom = v_size; + Left = Right = h_size; + } + GUIBorder(int32 top, int32 bottom, int32 left, int32 right) + { + Top = top; + Bottom = bottom; + Left = left; + Right = right; + } + void SetSize(int size) + { + Top = Bottom = Left = Right = size; + } + void SetSize(int h_size, int v_size) + { + Top = Bottom = v_size; + Left = Right = h_size; + } + + void SetSize(int32 top, int32 bottom, int32 left, int32 right) + { + Top = top; + Bottom = bottom; + Left = left; + Right = right; + } + }; +} +#endif // GUIBorder_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIButton.cpp b/LifeGUI/src/GUIButton.cpp new file mode 100644 index 0000000..cbf7f2e --- /dev/null +++ b/LifeGUI/src/GUIButton.cpp @@ -0,0 +1,152 @@ +#include "GUIManager.h" +#include "GUIButton.h" + +namespace LifeGUI +{ + GUIButton::GUIButton() + :pressed(false), checkable(false) + { + SetStyle(STR("Button")); + // skin = GUIManager::me()->skinDefault[STR("Button")]; + + caption = new GUIText(); + caption->SetPosition(LifeMath::Vec2i(0,0)); + caption->SetWidth(this->width); + caption->SetHAlign(HA_CENTER); + caption->SetVAlign(VA_CENTER); + caption->SetHeight(this->height); + caption->SetForeColor(LifeMath::float4(0.0f,0.0f,0.0f,1.0f)); + caption->SetBackColor(LifeMath::float4(0.0f,0.0f,0.0f,0.0f)); + this->SetBorderColor(LifeMath::float4(0.45f,0.45f,0.45f,1.0f)); + caption->SetText(STR("Unnamed button")); + caption->mouseEnabled = false; + + this->AddChild(caption); + } + + void GUIButton::OnMouseEnter() + { + if(!checkable) + { + this->state = "Hover"; + Invalidate(); + } + GUIWindow::OnMouseEnter(); + } + void GUIButton::Reset() + { + pressed = false; + state = STR("Normal"); + Invalidate(); + } + void GUIButton::OnMousePress(int x, int y, int key) + { + if(checkable) + { + pressed = !pressed; + + if(pressed) + { + state = STR("Hover"); + Invalidate(); + } + else + { + state = STR("Normal"); + Invalidate(); + } + } + + GUIWindow::OnMousePress(x, y, key); + } + void GUIButton::OnMouseRelease(int x, int y, int key) + { + GUIWindow::OnMouseRelease(x, y, key); + } + void GUIButton::OnMouseLeave() + { + if(!checkable) + { + this->state = "Normal"; + Invalidate(); + } + GUIWindow::OnMouseLeave(); + } + void GUIButton::SetForeColor(LifeMath::float4 color) + { + GUIPlane::SetForeColor(color); + caption->SetForeColor(color); + } + String GUIButton::GetCaption() + { + return caption->GetText(); + } + void GUIButton::SetSkin(GUIWidgetSkin* _skin) + { + skin = _skin; + } + void GUIButton::SetCaption(const String& text) + { + caption->SetText(text); + } + void GUIButton::SetWidth(int val) + { + width = val; + caption->SetWidth(val); + Invalidate(); + } + void GUIButton::SetHeight(int val) + { + height = val; + caption->SetHeight(val); + Invalidate(); + } + GUIButton::~GUIButton() + { + } + void GUIButton::ReleaseFocus() + { + Event_LostFocus.Invoke(this,0); + } + void GUIButton::Update(float dt) + { + /*if(InRect(GUIManager::me()->GetActiveOverlay()->cursorPos, this->GetPositionAbs(), this->width, this->height)) + { + if(!mouseCaptured && enabled) + { + mouseCaptured = true; + Event_MouseEnter.Invoke(this,0); + } + if(!PickChild(GUIManager::me()->GetActiveOverlay()->cursorPos)) + { + GUIManager::me()->GetActiveOverlay()->SelectFocus(this); + Event_GotFocus.Invoke(this,0); + } + } + else + { + if(mouseCaptured) + { + mouseCaptured = false; + Event_MouseLeave.Invoke(this,0); + } + }*/ + + // UpdateChilds(dt); + } + /*void GUIButton::Render() + { + if(invalid) + GenGeom(); + GUIManager::me()->GetRenderer()->SetPSTexture(skin->texture.ptr(), 0); + GUIManager::me()->GetRenderer()->SetVSConstantBuffer(cb, 0); + GUIManager::me()->GetRenderer()->SetVertexBuffer(vb); + + if(borderEnabled) + GUIManager::me()->GetRenderer()->Draw(6*9, 0); + else + GUIManager::me()->GetRenderer()->Draw(6, 0); + + RenderChilds(); + }*/ +} \ No newline at end of file diff --git a/LifeGUI/src/GUIButton.h b/LifeGUI/src/GUIButton.h new file mode 100644 index 0000000..cbcdeb7 --- /dev/null +++ b/LifeGUI/src/GUIButton.h @@ -0,0 +1,37 @@ +#ifndef GUIButton_h__ +#define GUIButton_h__ + +#include "GUIPlane.h" +#include "GUIText.h" + +namespace LifeGUI +{ + class GUIButton : public GUIPlane + { + private: + GUIText* caption; + bool checkable; + public: + void SetCheckable(bool state) {checkable = state;} + bool GetCheckable(){return checkable; } + bool pressed; + void OnMouseEnter(); + void OnMouseLeave(); + GUIButton(); + virtual ~GUIButton(); + void SetWidth(int val); + void SetHeight(int val); + void Update(float dt); + void KeyPress(int key, int modifier); + void SetSkin(GUIWidgetSkin* _skin); + void ReleaseFocus(); + void SetCaption(const String& text); + void SetForeColor(LifeMath::float4 color); + String GetCaption(); + + void Reset(); + void OnMousePress(int x, int y, int key); + void OnMouseRelease(int x, int y, int key); + }; +} +#endif // GUIButton_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUICheckBox.cpp b/LifeGUI/src/GUICheckBox.cpp new file mode 100644 index 0000000..421bc44 --- /dev/null +++ b/LifeGUI/src/GUICheckBox.cpp @@ -0,0 +1,110 @@ +#include "GUICheckBox.h" +#include "GUIManager.h" + +namespace LifeGUI +{ + GUICheckBox::GUICheckBox() + :_checked(false) + { + box = new GUIPlane(); + box->SetStyle(STR("Check")); + + box->SetWidth(19); + box->SetHeight(19); + this->AddChild(box); + + text = new GUIText(); + text->SetText(STR("CheckBox")); + text->SetPosition(LifeMath::Vec2i(box->GetWidth(), 0)); + text->SetAutoSize(true); + this->AddChild(text); + text->SetVAlign(VA_CENTER); + box->SetBorderEnabled(false); + text->SetBorderEnabled(false); + + box->Event_MouseClick += NewDelegate(this, &GUICheckBox::OnBoxClick); + + UpdateLayout(); + } + GUICheckBox::~GUICheckBox() + { + if(box) + delete box; + if(text) + delete text; + } + + void GUICheckBox::UpdateLayout() + { + int h1, h2; + + // + // + + h1 = box->GetHeight(); + h2 = text->GetHeight(); + + int text_offset = 5; + + if(h1 == h2) + { + this->SetHeight(h1); + box->SetPosition(LifeMath::Vec2i(0,0)); + text->SetPosition(LifeMath::Vec2i(box->GetWidth() + text_offset, 0)); + } + else if(h1 > h2) + { + this->SetHeight(h1); + box->SetPosition(LifeMath::Vec2i(0,0)); + text->SetPosition(LifeMath::Vec2i(box->GetWidth() + text_offset, (h1 - h2) / 2)); + } + else + { + this->SetHeight(h2); + box->SetPosition(LifeMath::Vec2i(0,(h2 - h1) / 2)); + text->SetPosition(LifeMath::Vec2i(box->GetWidth() + text_offset, 0)); + } + this->SetWidth(box->GetWidth() + text->GetWidth() + text_offset); + } + void GUICheckBox::OnBoxClick(GUIWindow* wnd, int v) + { + InternalSetChecked(!_checked); + } + void GUICheckBox::InternalSetChecked(bool state) + { + if(_checked != state) + { + _checked = state; + + if(_checked) + { + //box->skin_name = STR("Check:Checked:Back"); + box->state = "Pressed"; + } + else + { + box->state = "Normal"; + //box->skin_name = STR("Check:Unchecked:Back"); + } + + box->Invalidate(); + Event_CheckChanged.Invoke(this); + } + } + bool GUICheckBox::IsChecked() + { + return _checked; + } + void GUICheckBox::SetChecked(bool state) + { + InternalSetChecked(state); + } + void GUICheckBox::SetText(const String& _text) + { + text->SetText(_text); + } + const String& GUICheckBox::GetText() + { + return text->GetText(); + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUICheckBox.h b/LifeGUI/src/GUICheckBox.h new file mode 100644 index 0000000..447d402 --- /dev/null +++ b/LifeGUI/src/GUICheckBox.h @@ -0,0 +1,32 @@ +#ifndef GUICheckBox_h__ +#define GUICheckBox_h__ + +#include "GUIText.h" +#include "GUIPlane.h" + +namespace LifeGUI +{ + class GUICheckBox : public GUIWindow + { + public: + EVENT1(CheckEvent, GUICheckBox*); + + CheckEvent Event_CheckChanged; + + GUICheckBox(); + ~GUICheckBox(); + bool IsChecked(); + void SetChecked(bool state); + void SetText(const String& _text); + const String& GetText(); + private: + void InternalSetChecked(bool state); + void OnBoxClick(GUIWindow* wnd, int v); + bool _checked; + void UpdateLayout(); + GUIPlane* box; + GUIText* text; + }; +} + +#endif // GUICheckBox_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIComboBox.cpp b/LifeGUI/src/GUIComboBox.cpp new file mode 100644 index 0000000..bb19bc6 --- /dev/null +++ b/LifeGUI/src/GUIComboBox.cpp @@ -0,0 +1,114 @@ +#include "GUIComboBox.h" +#include "GUIManager.h" + +namespace LifeGUI +{ + GUIComboBox::GUIComboBox() + :list_hidden(true), hide_on_focus_lost(true) + { + this->SetHeight(20); + this->SetWidth(300); + + this->SetText(STR("640x480")); + this->SetForeColor(LifeMath::float4(0.74f,0.74f,0.74f,1)); + this->SetBackColor(LifeMath::float4(1,1,1,1)); + this->SetBorderColor(LifeMath::float4(0.15f,0.15f,0.15f,1.0f)); + // this->AddChild(text); + + this->SetStyle(STR("Combo")); + + arrowButton = new GUIPlane(); + this->AddChild(arrowButton); + arrowButton->SetStyle(STR("Combo_arrow")); + arrowButton->SetBorderEnabled(false); + arrowButton->SetBackColor(LifeMath::float4(1,1,1,1)); + //arrowButton->Hide(); + list = new GUIList(); + list->SetWidth(270); + list->SetHeight(100); + list->Hide(); + //this->AddChild(list); + + list->AddItem(STR("640x480"), 0); + list->AddItem(STR("800x600"), 0); + list->AddItem(STR("1024x768"), 0); + list->AddItem(STR("1280x1024"), 0); + list->AddItem(STR("1600x900"), 0); + list->AddItem(STR("1600x900"), 0); + list->AddItem(STR("100500"), 0); + + arrowButton->Event_MouseDown += NewDelegate(this, &GUIComboBox::onArrowClick); + list->Event_LostFocus += NewDelegate(this, &GUIComboBox::OnListLostFocus); + list->Event_ItemSelected += NewDelegate(this, &GUIComboBox::onItemSelected); + UpdateLayout(); + } + void GUIComboBox::UpdateLayout() + { + LifeMath::Rect2i r = this->GetClientRectangle(); + arrowButton->SetWidth(14); + arrowButton->SetHeight(r.height); + + arrowButton->SetPosition(LifeMath::Vec2i(r.width - r.height, 0)); + } + void GUIComboBox::onArrowClick(GUIWindow* sender, int args) + { + if(list_hidden) + { + if(hide_on_focus_lost) + ShowList(); + else + hide_on_focus_lost = true; + } + else + { + HideList(); + } + } + /*void GUIComboBox::ReleaseFocus() + { + list->Hide(); + Event_LostFocus.Invoke(this, 0); + }*/ + + void GUIComboBox::OnListLostFocus(GUIWindow* wnd, int p) + { + HideList(); + if(arrowButton->GetClientRectangleAbs().InRect(FindOverlay()->cursorPos)) + hide_on_focus_lost = false; + } + void GUIComboBox::ShowList() + { + if(list_hidden) + { + list_hidden = false; + LifeMath::Vec2i pos = this->GetPositionAbs(); + pos.Y+= this->GetHeight(); + list->SetPosition(pos); + list->Show(); + FindOverlay()->AddChild(list); + FindOverlay()->SetFocusWindow(list); + } + } + void GUIComboBox::HideList() + { + if(!list_hidden) + { + list_hidden = true; + list->Hide(); + FindOverlay()->RemoveChild(list->hwnd); + /* if(FindOverlay()->IsFocusWindow(list)) + { + FindOverlay()->SetFocusWindow(NULL); + }**/ + } + } + + GUIComboBox::~GUIComboBox() + { + } + void GUIComboBox::onItemSelected(GUIWindow* sender, const String& text, void* data) + { + this->SetText(text); + FindOverlay()->SetFocusWindow(NULL); + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUIComboBox.h b/LifeGUI/src/GUIComboBox.h new file mode 100644 index 0000000..013a5b6 --- /dev/null +++ b/LifeGUI/src/GUIComboBox.h @@ -0,0 +1,30 @@ +#ifndef GUIComboBox_h__ +#define GUIComboBox_h__ + +#include "GUIWindow.h" +#include "GUITextInput.h" +#include "GUIButton.h" +#include "GUIPlane.h" +#include "GUIList.h" + +namespace LifeGUI +{ + class GUIComboBox : public GUITextInput + { + public: + GUIComboBox(); + ~GUIComboBox(); + private: + bool hide_on_focus_lost; + bool list_hidden; + void UpdateLayout(); + GUIPlane* arrowButton; + GUIList* list; + void OnListLostFocus(GUIWindow* wnd, int p); + void ShowList(); + void HideList(); + void onItemSelected(GUIWindow* sender, const String& text, void* data); + void onArrowClick(GUIWindow* sender, int args); + }; +} +#endif // GUIComboBox_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIFileBrowser.cpp b/LifeGUI/src/GUIFileBrowser.cpp new file mode 100644 index 0000000..30aecb5 --- /dev/null +++ b/LifeGUI/src/GUIFileBrowser.cpp @@ -0,0 +1,142 @@ +#include "GUIFileBrowser.h" + +namespace LifeGUI +{ + GUIFileBrowser::GUIFileBrowser() + { + currentDir = STR("root"); + this->Event_Resize += NewDelegate(this, &GUIFileBrowser::OnResize); + backButton = new GUIButton(); + backButton->SetCaption(STR("...")); + backButton->SetBackColor(LifeMath::float4(1,0,0,1)); + this->AddChild(backButton); + backButton->Event_MouseClick += NewDelegate(this, &GUIFileBrowser::OnNavigateBack); + } + GUIFileBrowser::~GUIFileBrowser() + { + } + void GUIFileBrowser::OnNavigateBack(GUIWindow* wnd, int i) + { + LifeCore::IFolder* folder; + if(fileSystem->GetFolder(currentDir, &folder) == SC_OK) + { + /*if(fileSystem->GetFolder(folder->getPath(), &folder) == SC_OK) + { + ScanFolder(folder); + }*/ + + LifeCore::IFolder* parent = folder->getParent(); + if(parent) + { + SetDirectory(parent->getPath()); + //ScanFolder(parent); + } + else + { + SetDirectory(folder->getPath()); + //ScanFolder(folder); + } + } + } + void GUIFileBrowser::SetFileSystem(LifeCore::IFileSystem* fs) + { + fileSystem = fs; + LifeCore::IFolder* folder; + if(fileSystem->GetFolder(currentDir, &folder) == SC_OK) + { + ScanFolder(folder); + } + } + void GUIFileBrowser::OnFolderExpand(GUIWindow* wnd, int i) + { + printf("Folder expand begin!\n"); + int id = (int)wnd->UserData; + LifeCore::IFolder* f; + if(fileSystem->GetFolder(currentDir, &f) == SC_OK) + { + LifeCore::IFolder* sf = f->GetFolder(id); + SetDirectory(sf->getPath()); + } + printf("Folder expand end!\n"); + } + void GUIFileBrowser::ScanFolder(LifeCore::IFolder* folder) + { + for(FileCollectionItr it = filesToDelete.begin(); it != filesToDelete.end(); ++it) + { + delete (*it); + } + filesToDelete.clear(); + // + for(FileCollectionItr it = files.begin(); it != files.end(); ++it) + { + this->RemoveChild((*it)->hwnd); + filesToDelete.push_back((*it)); + //delete (*it); + } + files.clear(); + + for(int i = 0; i < folder->folderCount; i++) + { + LifeCore::IFolder* f = folder->GetFolder(i); + GUIButton* b = new GUIButton(); + this->AddChild(b); + files.push_back(b); + b->UserData = (void*)i; + b->Event_MouseClick += NewDelegate(this, &GUIFileBrowser::OnFolderExpand); + b->SetCaption(f->Name); + } + + for(int i = 0; i < folder->fileCount; i++) + { + LifeCore::IFile* f = folder->GetFile(i); + GUIButton* b = new GUIButton(); + this->AddChild(b); + files.push_back(b); + b->UserData = (void*)i; + b->SetBackColor(LifeMath::float4(0,1,0,1)); + b->Event_MouseClick += NewDelegate(this, &GUIFileBrowser::OnSelectFile); + //b->Event_MouseClick += NewDelegate(this, &GUIFileBrowser::OnFolderExpand); + b->SetCaption(f->FileName); + } + Format(); + } + void GUIFileBrowser::OnResize(GUIWindow* wnd) + { + Format(); + } + void GUIFileBrowser::Format() + { + int lineHeight = 19; + int cnt = files.size(); + + backButton->SetPosition(LifeMath::Vec2i(0,0)); + backButton->SetWidth(25); + backButton->SetHeight(lineHeight); + + for(int i = 0; i < cnt; i++) + { + GUIButton* b = files[i]; + b->SetHeight(lineHeight); + b->SetPosition(LifeMath::Vec2i(0, (i + 1) * lineHeight)); + b->SetWidth(this->GetClientRectangle().width); + } + } + void GUIFileBrowser::SetDirectory(const String& path) + { + currentDir = path; + LifeCore::IFolder* folder; + if(fileSystem->GetFolder(currentDir, &folder) == SC_OK) + { + ScanFolder(folder); + } + } + const String& GUIFileBrowser::GetSelectedFile() + { + return selectedFile; + } + void GUIFileBrowser::OnSelectFile(GUIWindow* wnd, int i) + { + selectedFile = currentDir + STR("/") + ((GUIButton*)wnd)->GetCaption(); + Event_SelectedFile.Invoke(this); + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUIFileBrowser.h b/LifeGUI/src/GUIFileBrowser.h new file mode 100644 index 0000000..50f0580 --- /dev/null +++ b/LifeGUI/src/GUIFileBrowser.h @@ -0,0 +1,38 @@ +#ifndef GUIFileBrowser_h__ +#define GUIFileBrowser_h__ + +#include "GUIScrollablePlane.h" +#include "GUIButton.h" +#include +#include + +namespace LifeGUI +{ + class GUIFileBrowser : public GUIScrollablePlane + { + public: + EVENT1(FileBrowserEvent, GUIFileBrowser*); + FileBrowserEvent Event_SelectedFile; + GUIFileBrowser(); + ~GUIFileBrowser(); + void SetFileSystem(LifeCore::IFileSystem* fs); + void SetDirectory(const String& path); + const String& GetSelectedFile(); + private: + void OnSelectFile(GUIWindow* wnd, int i); + void OnNavigateBack(GUIWindow* wnd, int i); + void OnFolderExpand(GUIWindow* wnd, int i); + void Format(); + void OnResize(GUIWindow* wnd); + void ScanFolder(LifeCore::IFolder* folder); + LifeCore::IFileSystem* fileSystem; + String currentDir; + String selectedFile; + typedef std::vector FileCollection; + typedef std::vector::iterator FileCollectionItr; + GUIButton* backButton; + FileCollection files; + FileCollection filesToDelete; + }; +} +#endif // GUIFileBrowser_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIFileBrowserDialog.cpp b/LifeGUI/src/GUIFileBrowserDialog.cpp new file mode 100644 index 0000000..27ffd04 --- /dev/null +++ b/LifeGUI/src/GUIFileBrowserDialog.cpp @@ -0,0 +1,34 @@ +#include "GUIFileBrowserDialog.h" + +namespace LifeGUI +{ + GUIFileBrowserDialog::GUIFileBrowserDialog() + { + browser = new GUIFileBrowser(); + this->AddChild(browser); + browser->SetPosition(LifeMath::Vec2i(5, 5)); + browser->SetBackColor(LifeMath::float4(0,1,0,1)); + browser->SetWidth(200); + browser->SetHeight(150); + browser->Event_SelectedFile += NewDelegate(this, &GUIFileBrowserDialog::OnSelectFile); + } + GUIFileBrowserDialog::~GUIFileBrowserDialog() + { + } + const String& GUIFileBrowserDialog::GetSelectedFile() + { + return browser->GetSelectedFile(); + } + void GUIFileBrowserDialog::OnSelectFile(GUIFileBrowser* browser) + { + this->Event_SelectedFile.Invoke(this); + } + void GUIFileBrowserDialog::SetFileSystem(LifeCore::IFileSystem* fs) + { + browser->SetFileSystem(fs); + } + void GUIFileBrowserDialog::SetDirectory(const String& path) + { + browser->SetDirectory(path); + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUIFileBrowserDialog.h b/LifeGUI/src/GUIFileBrowserDialog.h new file mode 100644 index 0000000..ca291b0 --- /dev/null +++ b/LifeGUI/src/GUIFileBrowserDialog.h @@ -0,0 +1,24 @@ +#ifndef GUIFileBrowserDialog_h__ +#define GUIFileBrowserDialog_h__ + +#include "GUIForm.h" +#include "GUIFileBrowser.h" + +namespace LifeGUI +{ + class GUIFileBrowserDialog : public GUIForm + { + public: + EVENT1(FileBrowserDialogEvent, GUIFileBrowserDialog*); + FileBrowserDialogEvent Event_SelectedFile; + GUIFileBrowserDialog(); + ~GUIFileBrowserDialog(); + void SetFileSystem(LifeCore::IFileSystem* fs); + void SetDirectory(const String& path); + const String& GetSelectedFile(); + private: + GUIFileBrowser* browser; + void OnSelectFile(GUIFileBrowser* browser); + }; +} +#endif // GUIFileBrowserDialog_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIFont.cpp b/LifeGUI/src/GUIFont.cpp new file mode 100644 index 0000000..7b74504 --- /dev/null +++ b/LifeGUI/src/GUIFont.cpp @@ -0,0 +1,137 @@ +#include "GUIFont.h" +#include "GUIManager.h" +#include +#include +#include FT_FREETYPE_H + +namespace LifeGUI +{ + int GUIFont::MeasureString(const std::wstring& text) + { + int size = text.size(); + const wchar_t* str = text.c_str(); + int stringSize = 0; + + if(size == 0) + return 0; + else if(size == 1) + { + return GetGlyph(str[0])->Width; + } + + for(int i = 0; i < size; i++) + { + if(str[i] == 0x0020) + { + stringSize += spaceSize; + } + else + { + GUIFontGlyph* glyph = GetGlyph(str[i]); + stringSize += glyph->hor_advance; + } + } + + return stringSize; + } + GUIFont::GUIFont(LifeCore::Range* charRange, int rangeCount){ + FT_Library ftLib; + FT_Face ftFontFace; + FT_Init_FreeType(&ftLib); + + LifeCore::IFile* font; + + GUIManager::me()->GetDataManager()->getFileSystem()->GetFile(AS_STRING("root/Data/Fonts/arial.ttf"), &font); + + if(!font){ + printf("Font not found!"); + return; + } + + LifeCore::IDataStreamPtr stm = font->OpenRead(); + printf("Font size: %i\n", stm->Length()); + FT_Byte* data = new FT_Byte[stm->Length()]; + + stm->Read(data,stm->Length()); + + //create font face + FT_New_Memory_Face(ftLib, data,stm->Length(),0, &ftFontFace); + + //font size (hardcoded yet : ( ) + int size = 10; + FT_Set_Char_Size(ftFontFace, size << 6, size << 6, 96, 96); + + //size of "space" character. I dunno where to get it, so i hardcoded it in my way )) + spaceSize = size / 4 * 3; + + stm.release(); + + //text string height + stringHeight = (ftFontFace->size->metrics.height >> 6); + + //hardcoded size of target font map. Baaaaad..... + int texW = 600, texH = 600; + font_tex = GUIManager::me()->GetDataManager()->createImage(texW, texH); + LifeCore::IDataRectangle* texData = font_tex->Map(0,0,1); + + texData->data.Clear(0xff); + + int xOffset = 0, yOffset = 0, curRowMaxHeight = 0; + + //render symbol ranges to texture + for(int rc = 0; rc glyph, FT_RENDER_MODE_NORMAL); + FT_Bitmap bm = ftFontFace->glyph->bitmap; + + if((xOffset + bm.width + 1) >= texW) + { + yOffset += stringHeight; + curRowMaxHeight = 0; + xOffset = 0; + } + + GUIFontGlyph* glyph = new GUIFontGlyph(); + glyph->Character = i; + glyph->X = xOffset + ftFontFace->glyph->bitmap_left; + glyph->Y = yOffset + (stringHeight - ftFontFace->glyph->bitmap_top); + glyph->Width = bm.width; + glyph->Height = bm.rows; + glyph->U = (float)glyph->X / (float)texW; + glyph->V = (float)glyph->Y / (float)texH; + glyph->TexW = (float)glyph->Width / (float)texW; + glyph->TexH = (float)glyph->Height / (float)texH; + glyph->Width = ftFontFace->glyph->metrics.width >> 6; + glyph->topOffset = ((ftFontFace->size->metrics.ascender) >> 6) - ((ftFontFace->glyph->metrics.horiBearingY) >> 6); + glyph->leftOffset = (ftFontFace->glyph->metrics.horiBearingX >> 6); + glyph->hor_advance = (ftFontFace->glyph->advance.x >> 6);\ + glyphs[static_cast(i)] = glyph; + + for(int ty = 0; ty < bm.rows; ty++) for(int tx = 0; tx < bm.width; tx++){ + int bigX = tx + xOffset + ftFontFace->glyph->bitmap_left; + int bigY = ty + yOffset + (stringHeight - ftFontFace->glyph->bitmap_top); + texData->data.Seek((bigY*(texData->pitch/4)+bigX)* font_tex->GetPixelSize()); + unsigned char tc = bm.buffer[(ty * bm.pitch + tx)]; + unsigned int color = 0x00ffffff | (tc << 24); + texData->data.Write(&color, 4); + } + + xOffset+= (ftFontFace->glyph->advance.x >> 6) + 1; + } + } + + FT_Done_Face(ftFontFace); + FT_Done_FreeType(ftLib); + + font_tex->Unmap(0); + delete[] data; + } + GUIFont::~GUIFont() + { + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUIFont.h b/LifeGUI/src/GUIFont.h new file mode 100644 index 0000000..277ec84 --- /dev/null +++ b/LifeGUI/src/GUIFont.h @@ -0,0 +1,37 @@ +#ifndef GUIFONT_h__ +#define GUIFONT_h__ + +//#include "Globals.h" +#include "Range.h" +#include "GUIFontGlyph.h" +#include "IImage.h" +#include +#include "LString.h" +#include + +namespace LifeGUI +{ + class GUIFont + { + public: + int stringHeight; + IImage* font_tex; + int trololo; + GUIFontGlyph* GetGlyph(wchar_t name) + { + GUIFontGlyph* g = glyphs[name]; + if(g) + return g; + std::map::iterator it = glyphs.begin(); + return it->second; + } + GUIFont(LifeCore::Range* charRange, int rangeCount); + ~GUIFont(); + int spaceSize; + int MeasureString(const std::wstring& text); + private: + std::map glyphs; + }; +} + +#endif \ No newline at end of file diff --git a/LifeGUI/src/GUIFontGlyph.h b/LifeGUI/src/GUIFontGlyph.h new file mode 100644 index 0000000..ecaa2b5 --- /dev/null +++ b/LifeGUI/src/GUIFontGlyph.h @@ -0,0 +1,20 @@ +#ifndef GUIFONTGLYPH_H__ +#define GUIFONTGLYPH_H__ + +namespace LifeGUI +{ + class GUIFontGlyph + { + public: + GUIFontGlyph(){} + ~GUIFontGlyph(){} + int Width, Height, X, Y; + float U, V, TexW, TexH; + int topOffset; + int leftOffset; + unsigned long Character; + int hor_advance; + }; +} + +#endif \ No newline at end of file diff --git a/LifeGUI/src/GUIFontManager.cpp b/LifeGUI/src/GUIFontManager.cpp new file mode 100644 index 0000000..3ba6475 --- /dev/null +++ b/LifeGUI/src/GUIFontManager.cpp @@ -0,0 +1,8 @@ +#include "GUIFontManager.h" + +namespace LifeGUI +{ + GUIFont* GUIFontManager::GetFont(const String& font_name){ + return nullptr; + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUIFontManager.h b/LifeGUI/src/GUIFontManager.h new file mode 100644 index 0000000..42b079d --- /dev/null +++ b/LifeGUI/src/GUIFontManager.h @@ -0,0 +1,22 @@ +#ifndef GUIFontManager_h__ +#define GUIFontManager_h__ + +#include +#include + +#include +#include "GUIFont.h" + +namespace LifeGUI +{ + class GUIFontManager{ + public: + GUIFontManager(); + GUIFont* GetFont(const String& font_name); + private: + typedef std::vector FontCollection; + typedef std::vector::iterator FontCollectionIt; + FontCollection fonts; + }; +} +#endif // GUIFontManager_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIForm.cpp b/LifeGUI/src/GUIForm.cpp new file mode 100644 index 0000000..8745cd3 --- /dev/null +++ b/LifeGUI/src/GUIForm.cpp @@ -0,0 +1,85 @@ +#include "GUIForm.h" +#include "GUIOverlay.h" + +namespace LifeGUI +{ + GUIForm::GUIForm() + :drag(false) + { + pText = new GUIText(); + pText->SetText(STR("Unnamed window")); + this->AddChild(pText); + pText->NonClientControl = true; + + Border.SetSize(5); + Border.Top = 24; + pText->SetPosition(LifeMath::Vec2i(0, -Border.Top)); + pText->SetHeight(Border.Top); + pText->SetVAlign(VA_CENTER); + pText->SetWidth(this->GetWidth()); + pText->skip_mouse = true; + this->Event_Resize += NewDelegate(this, &GUIForm::OnResize); + //pText->SetAutoSize(true); + } + GUIForm::~GUIForm() + { + } + void GUIForm::OnResize(GUIWindow* wnd) + { + pText->SetWidth(this->GetWidth()); + } + void GUIForm::SetText(const String& text) + { + pText->SetText(text); + } + void GUIForm::OnNCMousePress(int x, int y, int key) + { + LifeMath::Rect2i r = this->GetRectangleAbs(); + r.height = Border.Top; + + if(r.InRect(x, y))//click on window header + { + drag = true; + + dragOffset = LifeMath::Vec2i(x, y) - this->GetPositionAbs(); + FindOverlay()->GrabMouse(this); + } + else + { + drag = false; + } + GUIWindow::OnNCMousePress(x, y, key); + } + void GUIForm::OnNCMouseRelease(int x, int y, int key) + { + if(drag) + FindOverlay()->ReleaseMouse(); + drag = false; + GUIWindow::OnNCMouseRelease(x, y, key); + } + void GUIForm::OnMouseRelease(int x, int y, int key) + { + if(drag) + FindOverlay()->ReleaseMouse(); + drag = false; + GUIWindow::OnMouseRelease(x, y, key); + } + void GUIForm::OnNCMouseMove(int abs_x, int abs_y, int rel_x, int rel_y) + { + GUIWindow::OnNCMouseMove(abs_x, abs_y, rel_x, rel_y); + } + void GUIForm::OnMouseMove(int abs_x, int abs_y, int rel_x, int rel_y) + { + if(drag) + { + LifeMath::Vec2i pos = LifeMath::Vec2i(abs_x, abs_y) - dragOffset; + if(this->GetParent()) + { + pos = this->GetParent()->CursorToLocal(pos); + } + + this->SetPosition(pos); + } + GUIWindow::OnNCMouseMove(abs_x, abs_y, rel_x, rel_y); + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUIForm.h b/LifeGUI/src/GUIForm.h new file mode 100644 index 0000000..2e3387b --- /dev/null +++ b/LifeGUI/src/GUIForm.h @@ -0,0 +1,29 @@ +#ifndef GUIForm_h__ +#define GUIForm_h__ + +#include "GUIPlane.h" +#include "GUIText.h" +#include "GUIButton.h" + +namespace LifeGUI +{ + class GUIForm : public GUIPlane + { + public: + GUIForm(); + ~GUIForm(); + void SetText(const String& text); + private: + LifeMath::Vec2i dragOffset; + bool drag; + GUIText* pText; + GUIButton* pButtonClose; + void OnResize(GUIWindow* wnd); + void OnNCMousePress(int x, int y, int key); + void OnNCMouseRelease(int x, int y, int key); + void OnNCMouseMove(int abs_x, int abs_y, int rel_x, int rel_y); + void OnMouseMove(int abs_x, int abs_y, int rel_x, int rel_y); + void OnMouseRelease(int x, int y, int key); + }; +} +#endif // GUIForm_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIList.cpp b/LifeGUI/src/GUIList.cpp new file mode 100644 index 0000000..70062b1 --- /dev/null +++ b/LifeGUI/src/GUIList.cpp @@ -0,0 +1,85 @@ +#include "GUIList.h" + +namespace LifeGUI +{ + GUIList::GUIList() + :max_visible_items(4), AutoHeight(true) + { + NeedKeyFocus = true; + WindowStyle = GWS_POPUP; + this->SetBorderColor(LifeMath::float4(0.0f,0.0f,0.0f,1.0f)); + this->Event_Resize += NewDelegate(this, &GUIList::OnResizeList); + } + GUIList::~GUIList() + { + } + void GUIList::OnResizeList(GUIWindow* e) + { + int cnt = items.size(); + int w = this->GetClientRectangle().width; + for(int i = 0; i < cnt; i++) + { + items[i]->SetWidth(w); + } + } + void GUIList::itemMouseEnter(GUIWindow* e, int a) + { + GUIButton* b = (GUIButton*)e; + e->SetBackColor(LifeMath::float4(1,1,1,1)); + e->SetForeColor(LifeMath::float4(0.05f,0.05f,0.05f,1)); + //e->state = STR("Hover"); + //e->Invalidate(); + } + void GUIList::itemMouseLeave(GUIWindow* e, int a) + { + GUIButton* b = (GUIButton*)e; + e->SetBackColor(LifeMath::float4(1,1,1,1)); + e->SetForeColor(LifeMath::float4(1,1,1,1)); + //e->state = STR("Normal"); + //e->Invalidate(); + } + void GUIList::SetBackColor(const LifeMath::float4& color) + { + GUIScrollablePlane::SetBackColor(color); + } + void GUIList::SelectItem(GUIWindow* e, int a) + { + String text = ((GUIButton*)e)->GetCaption(); + this->Event_ItemSelected.Invoke(this, text, e->UserData); + } + void GUIList::ReleaseFocus() + { + Event_LostFocus.Invoke(this, 0); + } + void GUIList::AddItem(const String& text, void* data) + { + GUIButton* b = new GUIButton(); + b->SetWidth(this->GetClientRectangle().width); + b->SetHeight(18); + b->SetCaption(text); + b->SetBorderEnabled(false); + b->SetBackColor(LifeMath::float4(1,1,1,1)); + b->SetForeColor(LifeMath::float4(1,1,1,1)); + b->SetStyle(STR("Combo_list")); + b->UserData = data; + this->AddChild(b); + + int c = items.size(); + b->SetPosition(LifeMath::Vec2i(0, c * 18)); + b->Event_MouseDown += NewDelegate(this, &GUIList::SelectItem); + b->Event_MouseEnter += NewDelegate(this, &GUIList::itemMouseEnter); + b->Event_MouseLeave += NewDelegate(this, &GUIList::itemMouseLeave); + + items.push_back(b); + + if(AutoHeight) + { + if(items.size() <= max_visible_items) + this->SetHeight(items.size() * 18 + 2); + else + { + this->SetHeight(max_visible_items * 18 + 2); + } + } + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUIList.h b/LifeGUI/src/GUIList.h new file mode 100644 index 0000000..1dca7ae --- /dev/null +++ b/LifeGUI/src/GUIList.h @@ -0,0 +1,37 @@ +#ifndef GUIList_h__ +#define GUIList_h__ + +#include "GUIWindow.h" +#include "GUIPlane.h" +#include "GUIScrollablePlane.h" +#include "GUIButton.h" + +namespace LifeGUI +{ + class GUIList : public GUIScrollablePlane + { + public: + bool AutoHeight; + EVENT3(ListEvent, GUIWindow*, const String&, void*); + + ListEvent Event_ItemSelected; + + GUIList(); + ~GUIList(); + + void SetBackColor(const LifeMath::float4& color); + void ReleaseFocus(); + void AddItem(const String& text, void* data); + + private: + void OnResizeList(GUIWindow* e); + size_t max_visible_items; + typedef std::vector ItemList; + ItemList items; + void SelectItem(GUIWindow* e, int a); + void itemMouseEnter(GUIWindow* e, int a); + void itemMouseLeave(GUIWindow* e, int a); + }; +} + +#endif // GUIList_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIManager.cpp b/LifeGUI/src/GUIManager.cpp new file mode 100644 index 0000000..c9b2dd3 --- /dev/null +++ b/LifeGUI/src/GUIManager.cpp @@ -0,0 +1,440 @@ +#include "GUIManager.h" + +namespace LifeGUI +{ + IGUIRenderer* GUIManager::GetRenderer() + { + return guiRenderer; + } + IGUIDataManager* GUIManager::GetDataManager(){ + return guiDataManager; + } + void GUIManager::Init(IGUIRenderer* renderer, IGUIDataManager* data_manager) + { + guiRenderer = renderer; + guiDataManager = data_manager; + + IImage* main_skin; + main_skin = guiDataManager->getImage(STR("design_black_orange3.dds")); + //DIFF = main_skin; + DIFF = guiDataManager->getImage(AS_STRING("White.bmp")); + cursorTex = guiDataManager->getImage(AS_STRING("mouse.png")); + + LifeCore::IFile* sf; + guiDataManager->getFileSystem()->GetFile(String(STR("root/Data/GUI/Default.mmf")), &sf); + LifeCore::IDataStreamPtr stm = sf->OpenRead(); + + char buf[2]; + buf[1] = '\0'; + int str_num = 0; + String str; + int tw, th; + + String prev_skin; + GUIWidgetSkin* skin_tmp; + std::vector coll; + + while(stm->Read(&buf[0], 1)) + { + if(buf[0] == '\n') + { + if(str != STR("")) + { + String tmp; + //parse main skin texture info + if(str_num == 0) + { + size_t pos2; + size_t pos = str.find_first_of(STR(":")); + tmp = str.substr(0, pos); + tw = StrToInt(tmp); + pos++; + + pos2 = pos; + pos = str.find_first_of(STR(":"), pos2); + tmp = str.substr(pos2, pos); + th = StrToInt(tmp); + pos++; + pos2 = pos; + + //pos = str.find_first_of(STR(":"), pos2); + tmp = str.substr(pos2, str.size()); + //main_skin = guiRenderer->GetImage(tmp); + } + else + {// + coll.clear(); + SplitString(str, &coll, ':'); + + //new skin! + if(prev_skin != coll[0]) + { + prev_skin = coll[0]; + if(coll[0] != STR("Cursor")) + skin_tmp = new GUIWidgetSkin(main_skin); + else + skin_tmp = new GUIWidgetSkin(cursorTex); + skinDefault[coll[0]] = skin_tmp; + } + + int i1,i2,i3,i4; + i1 = StrToInt(coll[3]); + i2 = StrToInt(coll[4]); + i3 = StrToInt(coll[5]); + i4 = StrToInt(coll[6]); + + //if(!skin_tmp->GetState(coll[1])) + //{ + skin_tmp->AddState(coll[1]); + //} + + skin_tmp->states[coll[1]]->AddElement(coll[2], LifeMath::Vec4i(i1, i2, i3, i4)); + + + } + } + + str_num++; + str= STR(""); + } + else + { + str = str + String(&buf[0]); + } + } + + //create default skin + GUIWidgetSkin* skin = new GUIWidgetSkin(guiDataManager->getImage(STR("Button.bmp"))); + + /* GUISkinState* normal = skin->AddState("Normal"); + //border defines + normal->AddElement("BorderLeft", LifeMath::Vec4i(5,6,7,9)); + normal->AddElement("BorderRight", LifeMath::Vec4i(12,6,14,9)); + normal->AddElement("BorderTop", LifeMath::Vec4i(8,3,11,5)); + normal->AddElement("BorderBottom", LifeMath::Vec4i(8,10,11,12)); + normal->AddElement("CornerTopLeft", LifeMath::Vec4i(5,3,7,5)); + normal->AddElement("CornerTopRight", LifeMath::Vec4i(12,3,14,5)); + normal->AddElement("CornerBottomLeft", LifeMath::Vec4i(5,10,7,12)); + normal->AddElement("CornerBottomRight", LifeMath::Vec4i(12,10,14,12)); + normal->AddElement("Background", LifeMath::Vec4i(30,32,35,57)); + + skinDefault[STR("Button")] = skin;*/ + + LifeCore::Range r[] = + { + LifeCore::Range(0x0410, 0x044f),//russian characters + LifeCore::Range(0x0041, 0x007A),//latin characters + LifeCore::Range(0x0019, 0x002f), //space + LifeCore::Range(0x0030, 0x0040) //space + }; + + font = new GUIFont(r, 4); + } + int GUIManager::GetNewID() + { + return lastID++; + } + GUIOverlay* GUIManager::GetActiveOverlay() + { + return curOverlay; + } + /*bool GUIManager::KeyDown( LifeCore::KeyCodes::Key key, unsigned int text, const std::string& str ) + { + + + if(curOverlay) + curOverlay->OnKeyDown(key, text, str); + return true; + //return curOverlay->KeyDown(key, text); + } + bool GUIManager::KeyUp( LifeCore::KeyCodes::Key key, unsigned int text, const std::string& str ) + { + return true; + } + bool GUIManager::MouseMove( int absX, int relX, int absY, int relY) + { + if(curOverlay) + { + curOverlay->OnMouseMove(absX, absY, relX, relY); + } + + return true; + } + bool GUIManager::MousePressed( LifeCore::KeyCodes::Key button) + { + if(curOverlay) + { + + + curOverlay->OnMousePress(curOverlay->cursorPos.X, curOverlay->cursorPos.Y, button); + } + return false; + } + bool GUIManager::MouseReleased( LifeCore::KeyCodes::Key button) + { + if(curOverlay) + { + + + curOverlay->OnMouseRelease(curOverlay->cursorPos.X, curOverlay->cursorPos.Y, button); + } + return false; + }*/ + + + int GUIManager::GetWidth() + { + return scrW; + } + int GUIManager::GetHeight() + { + return scrH; + } + + int GUIManager::GenQuad(int layer, const LifeMath::Rect2i& scissor, LifeMath::Vec2i position, int width, int height, LifeGUI::GUIVertex* pBuffer, LifeMath::float2 tile) + { + LifeMath::float4 color(1.0f, 1.0f, 1.0f, 1.0f); + + return GUIManager::GenQuad(layer, scissor, position, width, height, pBuffer, 0.0f, 0.0f, 1.0f, 1.0f,color,color,color,color, tile); + } + int GUIManager::GenQuad(int layer, const LifeMath::Rect2i& scissor, LifeMath::Vec2i position, int width, int height, LifeGUI::GUIVertex* pBuffer, const LifeMath::float4& col, LifeMath::float2 tile) + { + return GUIManager::GenQuad(layer, scissor, position, width, height, pBuffer, 0.0f, 0.0f, 1.0f, 1.0f,col,col,col,col, tile); + } + int GUIManager::GenQuad(int layer, const LifeMath::Rect2i& scissor, LifeMath::Vec2i position, int width, int height, LifeGUI::GUIVertex* pBuffer, const LifeMath::float4& col, float texOffX, float texOffY, float texWX, float texWY, LifeMath::float2 tile) + { + return GUIManager::GenQuad(layer, scissor, position, width, height, pBuffer, texOffX, texOffY, texWX, texWY,col,col,col,col,tile); + } + + int GUIManager::GenQuad(int layer, const LifeMath::Rect2i& scissor, LifeMath::Vec2i position, int width, int height, LifeGUI::GUIVertex* pBuffer, float texOffX, float texOffY, float texWX, float texWY, LifeMath::float2 tile) + { + LifeMath::float4 color(1.0f, 1.0f, 1.0f, 1.0f); + return GUIManager::GenQuad(layer, scissor, position, width, height, pBuffer, texOffX, texOffY, texWX, texWY,color,color,color,color, tile); + } + + int GUIManager::GenQuad(int layer, const LifeMath::Rect2i& scissor, LifeMath::Vec2i position, int width, int height, LifeGUI::GUIVertex* pBuffer, float texOffX, float texOffY, float texWX, float texWY, const LifeMath::float4& color0, const LifeMath::float4& color1, const LifeMath::float4& color2, const LifeMath::float4& color3, const LifeMath::float2& tile) + { + //if quad is outside of scissor + if(position.X > scissor.GetMaxX()) return 0; + if(position.Y > scissor.GetMaxY()) return 0; + if((position.X + width) < scissor.x) return 0; + if((position.Y + height) < scissor.y) return 0; + + //cut left side + if(scissor.x > position.X) + { + int offset = scissor.x - position.X; + float pc = float(offset) / (float)width; + + position.X += offset; + width -= offset; + + float to = texWX * pc; + texOffX += to; + texWX -= to; + } + + //cut right side + if(scissor.GetMaxX() < (position.X + width)) + { + int offset = (position.X + width) - scissor.GetMaxX(); + float pc = float(offset) / (float)width; + + width -= offset; + + float to = texWX * pc; + texWX -= to; + } + + //cut top side + if(scissor.y > position.Y) + { + int offset = scissor.y - position.Y; + float pc = float(offset) / (float)height; + + position.Y += offset; + height -= offset; + + float to = texWY * pc; + texOffY += to; + texWY -= to; + } + + //cut bottom side + if(scissor.GetMaxY() < (position.Y + height)) + { + int offset = (position.Y + height) - (scissor.GetMaxY()); + float pc = float(offset) / (float)height; + + height -= offset; + + float to = texWY * pc; + texWY -= to; + } + + //width -=1; + //height -=1; + pBuffer[0] = GUIVertex(LifeMath::float3(static_cast(position.X), static_cast(position.Y), (float)layer), color0, LifeMath::float4(0, 0, tile.X, tile.Y), LifeMath::float4(texOffX, texOffY, texWX, texWY)); //0 + pBuffer[1] = GUIVertex(LifeMath::float3(static_cast(position.X + width), static_cast(position.Y), (float)layer), color1, LifeMath::float4(tile.X, 0, tile.X, tile.Y), LifeMath::float4(texOffX, texOffY, texWX, texWY)); //1 + pBuffer[2] = GUIVertex(LifeMath::float3(static_cast(position.X + width),static_cast( position.Y + height), (float)layer), color2, LifeMath::float4(tile.X, tile.Y, tile.X, tile.Y), LifeMath::float4(texOffX, texOffY, texWX, texWY)); //2 + + pBuffer[3] = GUIVertex(LifeMath::float3(static_cast(position.X), static_cast(position.Y), (float)layer), color0, LifeMath::float4(0, 0, tile.X, tile.Y), LifeMath::float4(texOffX, texOffY, texWX, texWY)); //1 + pBuffer[4] = GUIVertex(LifeMath::float3(static_cast(position.X + width), static_cast(position.Y + height), (float)layer), color2, LifeMath::float4(tile.X, tile.Y, tile.X, tile.Y), LifeMath::float4(texOffX, texOffY, texWX, texWY)); //2 + pBuffer[5] = GUIVertex(LifeMath::float3(static_cast(position.X), static_cast(position.Y + height), (float)layer), color3, LifeMath::float4(0, tile.Y, tile.X, tile.Y), LifeMath::float4(texOffX, texOffY, texWX, texWY)); //3 + return 6; + } + + int GUIManager::GenFrame(int layer, const LifeMath::Rect2i& scissor, int width, int height, LifeGUI::GUIBorder* border, const LifeMath::float4& color, LifeGUI::GUIVertex* pBuffer) + { + return GenFrame(layer, scissor, 0, 0, width, height, border, color, pBuffer); + } + int GUIManager::GenFrame(int layer, const LifeMath::Rect2i& scissor, int x, int y,int width, int height, LifeGUI::GUIBorder* border, const LifeMath::float4& color, LifeGUI::GUIVertex* pBuffer) + { + //1 2 3 + // + //4 5 + // + //6 7 8 + + LifeMath::Vec2i pos(x, y); + int offset = 0; + //top left corner + offset += GenQuad(layer, scissor, LifeMath::Vec2i(0,0) + pos, border->Left, border->Top, pBuffer + offset, color, 0.0f, 0.0f,0.0f,0.0f); + //top center + offset += GenQuad(layer, scissor, LifeMath::Vec2i(border->Left,0) + pos, width - (border->Left + border->Right), border->Top, pBuffer + offset, color, 0.0f, 0.0f,0.0f,0.0f); + //top right corner + offset += GenQuad(layer, scissor, LifeMath::Vec2i(width - border->Right, 0) + pos, border->Right, border->Top, pBuffer + offset, color, 0.0f, 0.0f,0.0f,0.0f); + + //left center + offset += GenQuad(layer, scissor, LifeMath::Vec2i(0, border->Top) + pos, border->Left, height - (border->Top + border->Bottom), pBuffer + offset, color, 0.0f, 0.0f,0.0f,0.0f); + //right center + offset += GenQuad(layer, scissor, LifeMath::Vec2i(width - border->Right, border->Top) + pos, border->Right, height - (border->Top + border->Bottom), pBuffer + offset, color, 0.0f, 0.0f,0.0f,0.0f); + + //bottom left corner + offset += GenQuad(layer, scissor, LifeMath::Vec2i(0,height - border->Bottom ) + pos, border->Left, border->Bottom, pBuffer + offset, color, 0.0f, 0.0f,0.0f,0.0f); + + //bottom center + offset += GenQuad(layer, scissor, LifeMath::Vec2i(border->Left,height - border->Bottom) + pos, width - (border->Left + border->Right), border->Bottom, pBuffer + offset, color, 0.0f, 0.0f,0.0f,0.0f); + + //bottom right corner + offset += GenQuad(layer, scissor, LifeMath::Vec2i(width - border->Right, height - border->Bottom) + pos, border->Right, border->Bottom, pBuffer + offset, color, 0.0f, 0.0f,0.0f,0.0f); + + return offset; + } + int GUIManager::GenFrame(int layer, const LifeMath::Rect2i& scissor, int width, int height, int borderSize, const LifeMath::float4& color, LifeGUI::GUIVertex* pBuffer, LifeGUI::GUIWidgetSkin* skin) + { + return GenFrame(layer, scissor, 0, 0, width, height, borderSize, color, pBuffer, skin); + } + int GUIManager::GenFrame(int layer, const LifeMath::Rect2i& scissor, int x, int y, int width, int height, int borderSize, const LifeMath::float4& color, LifeGUI::GUIVertex* pBuffer, LifeGUI::GUIWidgetSkin* skin) + { + //1 2 3 + // + //4 5 + // + //6 7 8 + GUISkinState* normalState = skin->GetState("Normal"); + LifeMath::float4 bl = normalState->GetElement("BorderLeft")->Mapping; + LifeMath::float4 br = normalState->GetElement("BorderRight")->Mapping; + LifeMath::float4 bt = normalState->GetElement("BorderTop")->Mapping; + LifeMath::float4 bb = normalState->GetElement("BorderBottom")->Mapping; + + LifeMath::float4 ctl = normalState->GetElement("CornerTopLeft")->Mapping; + LifeMath::float4 ctr = normalState->GetElement("CornerTopRight")->Mapping; + + LifeMath::float4 cbl = normalState->GetElement("CornerBottomLeft")->Mapping; + LifeMath::float4 cbr = normalState->GetElement("CornerBottomRight")->Mapping; + + LifeMath::Vec2i pos(x, y); + + int offset = 0; + + offset += GenQuad(layer, scissor, LifeMath::Vec2i(0,0) + pos, borderSize, borderSize, pBuffer + offset, color, ctl.X, ctl.Y, ctl.Z, ctl.W); + offset += GenQuad(layer, scissor, LifeMath::Vec2i(borderSize,0) + pos, width - borderSize * 2, borderSize, pBuffer + offset, color, bt.X, bt.Y, bt.Z, bt.W); + offset += GenQuad(layer, scissor, LifeMath::Vec2i(width - borderSize, 0) + pos, borderSize, borderSize, pBuffer + offset, color, ctr.X, ctr.Y, ctr.Z, ctr.W); + + offset += GenQuad(layer, scissor, LifeMath::Vec2i(0, borderSize) + pos, borderSize, height - borderSize * 2, pBuffer + offset, color, bl.X, bl.Y, bl.Z, bl.W); + offset += GenQuad(layer, scissor, LifeMath::Vec2i(width - borderSize, borderSize) + pos, borderSize, height - borderSize * 2, pBuffer + offset, color, br.X, br.Y, br.Z, br.W); + + offset += GenQuad(layer, scissor, LifeMath::Vec2i(0,height - borderSize) + pos, borderSize, borderSize, pBuffer + offset, color, cbl.X, cbl.Y, cbl.Z, cbl.W); + offset += GenQuad(layer, scissor, LifeMath::Vec2i(borderSize,height - borderSize) + pos, width - borderSize * 2, borderSize, pBuffer + offset, color, bb.X, bb.Y, bb.Z, bb.W); + offset += GenQuad(layer, scissor, LifeMath::Vec2i(width - borderSize, height - borderSize) + pos, borderSize, borderSize, pBuffer + offset, color, cbr.X, cbr.Y, cbr.Z, cbr.W); + + return offset; + } + + /*int GUIManager::GenQuad(int layer, const LifeMath::Rect2i& scissor, LifeMath::Vec2i position, int width, int height, LifeGUI::GUIVertex* pBuffer, LifeMath::float2 tile){ + return 0; + } + int GUIManager::GenQuad(int layer, const LifeMath::Rect2i& scissor, LifeMath::Vec2i position, int width, int height, LifeGUI::GUIVertex* pBuffer, const LifeMath::float4& col, LifeMath::float2 tile){ + return 0; + } + int GUIManager::GenQuad(int layer, const LifeMath::Rect2i& scissor, LifeMath::Vec2i position, int width, int height, LifeGUI::GUIVertex* pBuffer, const LifeMath::float4& col, float texOffX, float texOffY, float texWX, float texWY, LifeMath::float2 tile){ + return 0; + } + int GUIManager::GenQuad(int layer, const LifeMath::Rect2i& scissor, LifeMath::Vec2i position, int width, int height, LifeGUI::GUIVertex* pBuffer, float texOffX, float texOffY, float texWX, float texWY, LifeMath::float2 tile){ + return 0; + } + int GUIManager::GenQuad(int layer, const LifeMath::Rect2i& scissor, LifeMath::Vec2i position, int width, int height, LifeGUI::GUIVertex* pBuffer, float texOffX, float texOffY, float texWX, float texWY, const LifeMath::float4& color0, const LifeMath::float4& color1, const LifeMath::float4& color2, const LifeMath::float4& color3, const LifeMath::float2& tile){ + return 0; + } + int GUIManager::GenFrame(int layer, const LifeMath::Rect2i& scissor, int width, int height, LifeGUI::GUIBorder* border, const LifeMath::float4& color, LifeGUI::GUIVertex* pBuffer){ + return 0; + } + int GUIManager::GenFrame(int layer, const LifeMath::Rect2i& scissor, int x, int y,int width, int height, LifeGUI::GUIBorder* border, const LifeMath::float4& color, LifeGUI::GUIVertex* pBuffer){ + return 0; + } + int GUIManager::GenFrame(int layer, const LifeMath::Rect2i& scissor, int width, int height, int borderSize, const LifeMath::float4& color, LifeGUI::GUIVertex* pBuffer, LifeGUI::GUIWidgetSkin* skin){ + return 0; + } + int GUIManager::GenFrame(int layer, const LifeMath::Rect2i& scissor, int x, int y, int width, int height, int borderSize, const LifeMath::float4& color, LifeGUI::GUIVertex* pBuffer, LifeGUI::GUIWidgetSkin* skin){ + return 0; + }*/ + + + + + void GUIManager::Update(float dt) + { + if(curOverlay) + curOverlay->Update(dt); + } + void GUIManager::Render() + { + guiRenderer->Reset(); + if(curOverlay) + curOverlay->Render(); + guiRenderer->Render(); + } + void GUIManager::AddOverlay(const String& name) + { + GUIOverlay* ovrl = new GUIOverlay(name); + ovrl->SetPosition(LifeMath::Vec2i(0,0)); + ovrl->SetWidth(this->GetWidth()); + ovrl->SetHeight(this->GetHeight()); + if(!overlays.size()) + curOverlay = ovrl; + overlays.push_back(ovrl); + } + + LifeGUI::GUIOverlay* GUIManager::GetOverlay(const String& name) + { + for(OverlayList::iterator it = overlays.begin(); it != overlays.end(); ++it) + { + if( (*it)->name == name) + return *it; + } + return 0; + } + + void GUIManager::SelectOverlay(const String& name) + { + for(OverlayList::iterator it = overlays.begin(); it != overlays.end(); ++it) + { + if( (*it)->name == name) + { + curOverlay = *it; + return; + } + } + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUIManager.h b/LifeGUI/src/GUIManager.h new file mode 100644 index 0000000..2b577ba --- /dev/null +++ b/LifeGUI/src/GUIManager.h @@ -0,0 +1,67 @@ +#ifndef GUIMANAGER_H__ +#define GUIMANAGER_H__ + +#include "Singleton.h" +#include "GUIOverlay.h" +#include "GUIWindow.h" +#include "GUIFont.h" +#include "GUIVertex.h" +#include +#include "Types.h" +#include "GUIWidgetSkin.h" +#include "IInputSystem.h" +#include "GUIMessaging.h" +#include +#include "IGUIRenderer.h" +#include "IGUIDataManager.h" + +namespace LifeGUI +{ + class GUIManager : public Singleton + { + friend class Singleton; + private: + int lastID; + LifeMath::Vec2i cursorPos; + int scrW; + int scrH; + typedef std::vector OverlayList; + OverlayList overlays; + GUIOverlay* curOverlay; + IGUIRenderer* guiRenderer; + IGUIDataManager* guiDataManager; + GUIManager() + :curOverlay(0), lastID(0), guiRenderer(nullptr), guiDataManager(nullptr) + { + } + public: + IGUIRenderer* GetRenderer(); + IGUIDataManager* GetDataManager(); + void Init(IGUIRenderer* renderer, IGUIDataManager* data_manager); + int GetNewID(); + GUIOverlay* GetActiveOverlay(); + + std::map skinDefault; + IImage* DIFF; + IImage* cursorTex; + GUIFont* font; + int GetWidth(); + int GetHeight(); + int GenQuad(int layer, const LifeMath::Rect2i& scissor, LifeMath::Vec2i position, int width, int height, LifeGUI::GUIVertex* pBuffer, LifeMath::float2 tile = LifeMath::float2(1,1)); + int GenQuad(int layer, const LifeMath::Rect2i& scissor, LifeMath::Vec2i position, int width, int height, LifeGUI::GUIVertex* pBuffer, const LifeMath::float4& col, LifeMath::float2 tile = LifeMath::float2(1,1)); + int GenQuad(int layer, const LifeMath::Rect2i& scissor, LifeMath::Vec2i position, int width, int height, LifeGUI::GUIVertex* pBuffer, const LifeMath::float4& col, float texOffX, float texOffY, float texWX, float texWY, LifeMath::float2 tile = LifeMath::float2(1,1)); + int GenQuad(int layer, const LifeMath::Rect2i& scissor, LifeMath::Vec2i position, int width, int height, LifeGUI::GUIVertex* pBuffer, float texOffX, float texOffY, float texWX, float texWY, LifeMath::float2 tile = LifeMath::float2(1,1)); + int GenQuad(int layer, const LifeMath::Rect2i& scissor, LifeMath::Vec2i position, int width, int height, LifeGUI::GUIVertex* pBuffer, float texOffX, float texOffY, float texWX, float texWY, const LifeMath::float4& color0, const LifeMath::float4& color1, const LifeMath::float4& color2, const LifeMath::float4& color3, const LifeMath::float2& tile); + int GenFrame(int layer, const LifeMath::Rect2i& scissor, int width, int height, LifeGUI::GUIBorder* border, const LifeMath::float4& color, LifeGUI::GUIVertex* pBuffer); + int GenFrame(int layer, const LifeMath::Rect2i& scissor, int x, int y,int width, int height, LifeGUI::GUIBorder* border, const LifeMath::float4& color, LifeGUI::GUIVertex* pBuffer); + int GenFrame(int layer, const LifeMath::Rect2i& scissor, int width, int height, int borderSize, const LifeMath::float4& color, LifeGUI::GUIVertex* pBuffer, LifeGUI::GUIWidgetSkin* skin); + int GenFrame(int layer, const LifeMath::Rect2i& scissor, int x, int y, int width, int height, int borderSize, const LifeMath::float4& color, LifeGUI::GUIVertex* pBuffer, LifeGUI::GUIWidgetSkin* skin); + void Update(float dt); + void Render(); + void AddOverlay(const String& name); + LifeGUI::GUIOverlay* GetOverlay(const String& name); + void SelectOverlay(const String& name); + }; +} + +#endif \ No newline at end of file diff --git a/LifeGUI/src/GUIMenu.cpp b/LifeGUI/src/GUIMenu.cpp new file mode 100644 index 0000000..da05a7f --- /dev/null +++ b/LifeGUI/src/GUIMenu.cpp @@ -0,0 +1,237 @@ +#include "GUIMenu.h" +#include "GUIButton.h" +#include "GUIOverlay.h" + +namespace LifeGUI +{ + GUIMenu::GUIMenu(bool allowFocus) + :expand_id(-1), parentMenu(0) + { + //NeedKeyFocus = allowFocus; + NeedKeyFocus = true; + this->SetWidth(150); + this->SetHeight(10); + + this->Event_LostFocus += NewDelegate(this, &GUIMenu::OnLostFocus); + //this->Event_Resize += NewDelegate(this, &GUIMenu::OnResize); + } + GUIMenu::~GUIMenu() + { + } + void GUIMenu::RemoveItems() + { + for(ItemCollectionItr it = Items.begin(); it != Items.end(); ++it) + { + delete (*it); + } + Items.clear(); + RebuildControls(); + UpdateLayout(); + } + bool GUIMenu::IsChildMenu(GUIWindow* m) + { + if(expand_id < 0) + { + return false; + } + else + { + GUIMenu* menu = Items[expand_id]->GetSubmenu(); + if(m == menu) + return true; + else + return menu->IsChildMenu(m); + } + } + GUIMenu* GUIMenu::FindRootMenu() + { + if(parentMenu) + { + return parentMenu->FindRootMenu(); + } + else + { + return this; + } + } + void GUIMenu::SetParentMenu(GUIMenu* p) + { + parentMenu = p; + } + void GUIMenu::OnLostFocus(GUIWindow* wnd, int arg) + { + GUIWindow* newFocus = FindOverlay()->GetFocusWindow(); + //if focus gained not by the child menu - auto hide! + if(!IsChildMenu(newFocus)) + { + OnCollapse(); + } + } + void GUIMenu::OnSelectElement(GUIWindow* wnd, int arg) + { + FindOverlay()->SetFocusWindow(0); + GUIButton* b = (GUIButton*)wnd; + + GUIMenuItem* itm = (GUIMenuItem*)b->UserData; + itm->Event_Select.Invoke(itm); + + printf("Selected menu item! Text: %s\n", b->GetCaption().c_str()); + FindRootMenu()->Event_SelectItem.Invoke(this, 0); + printf("OMFG Set up item index!!!\n"); + FindRootMenu()->OnCollapse(); + } + void GUIMenu::OnCollapse() + { + CollapseSubmenu(); + if(parent) + parent->RemoveChild(this->ID); + Event_Collapse.Invoke(this); + } + + GUIMenuItem* GUIMenu::AddItem(const String& text, bool hasSubmenu) + { + GUIMenuItem* itm = new GUIMenuItem(text, hasSubmenu); + + Items.push_back(itm); + + if(itm->HasSubmenu()) + itm->GetSubmenu()->SetParentMenu(this); + + RebuildControls(); + UpdateLayout(); + return itm; + } + void GUIMenu::OnResize(GUIWindow* wnd) + { + UpdateLayout(); + } + void GUIMenu::UpdateLayout() + { + LifeMath::Rect2i r = this->GetClientRectangle(); + + int offset = 0; + int item_h = 20; + for(WindowListIter it = internal_windows.begin(); it != internal_windows.end(); ++it) + { + GUIWindow* itm = (*it); + //itm->SetWidth(r.width); + //itm->SetHeight(item_h); + itm->SetPosition(LifeMath::Vec2i(0, offset * item_h)); + + offset++; + } + + int h = offset*item_h; + if(IsBorderEnabled()) + h+=1*2; + this->SetHeight(h); + } + void GUIMenu::CollapseSubmenu() + { + if(expand_id >=0) + { + Items[expand_id]->GetSubmenu()->CollapseSubmenu(); + FindOverlay()->RemoveChild(Items[expand_id]->GetSubmenu()->ID); + expand_id = -1; + } + } + void GUIMenu::OnHoverElement(GUIWindow* wnd, int arg) + { + GUIMenuItem* itm = reinterpret_cast(wnd->UserData); + if(expand_id >= 0) + { + if(GetItemID(itm) != expand_id) + { + CollapseSubmenu(); + } + } + } + void GUIMenu::OnExpandMenu(GUIWindow* wnd, int arg) + { + GUIMenuItem* itm = reinterpret_cast(wnd->UserData); + + if(expand_id >=0) //already expanded + { + int itm_id = GetItemID(itm); + if(expand_id == itm_id) + { + CollapseSubmenu(); + return; + } + else + CollapseSubmenu(); + } + + GUIButton* b = reinterpret_cast(wnd); + FindOverlay()->AddChild(itm->GetSubmenu()); + expand_id = GetItemID(itm); + + itm->GetSubmenu()->Show(); + LifeMath::Vec2i pos = b->GetRectangleAbs().GetMax(); + pos.Y -= b->GetHeight(); + itm->GetSubmenu()->SetPosition(pos); + } + size_t GUIMenu::GetItemID(GUIMenuItem* itm) + { + for(int i = 0; i < Items.size(); i++) + { + if(Items[i] == itm) + return i; + } + return -1; + } + void GUIMenu::RebuildControls() + { + int itm_cnt = Items.size(); + LifeMath::Rect2i r = this->GetClientRectangle(); + + for(WindowListIter it = internal_windows.begin(); it != internal_windows.end(); ++it) + { + this->RemoveChild((*it)->ID); + delete (*it); + } + + for(WindowListIter it = internal_arrows.begin(); it != internal_arrows.end(); ++it) + { + delete (*it); + } + + internal_windows.clear(); + internal_arrows.clear(); + + int item_h = 20; + for(ItemCollectionItr it = Items.begin(); it != Items.end(); it++) + { + GUIButton* b = new GUIButton(); + b->UserData = (*it); + b->SetCaption((*it)->GetText()); + internal_windows.push_back(b); + this->AddChild(b); + b->SetWidth(r.width); + b->SetHeight(item_h); + b->SetBorderEnabled(false); + b->Event_MouseEnter += NewDelegate(this, &GUIMenu::OnHoverElement); + b->SetStyle(STR("Combo_list")); + if(!(*it)->HasSubmenu()) + b->Event_MouseDown += NewDelegate(this, &GUIMenu::OnSelectElement); + + // + if((*it)->HasSubmenu()) + { + b->Event_MouseDown += NewDelegate(this, &GUIMenu::OnExpandMenu); + + //add arrow + GUIPlane* arrow = new GUIPlane(); + arrow->SetWidth(14); + arrow->SetHeight(14); + arrow->state = STR("Normal"); + arrow->SetStyle(STR("Menu_arrow")); + arrow->skip_mouse = true; + arrow->SetBorderEnabled(false); + b->AddChild(arrow); + internal_arrows.push_back(arrow); + arrow->SetPosition(LifeMath::Vec2i(b->GetClientRectangle().width - 14, (b->GetClientRectangle().height - 14) / 2)); + } + } + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUIMenu.h b/LifeGUI/src/GUIMenu.h new file mode 100644 index 0000000..e3ac254 --- /dev/null +++ b/LifeGUI/src/GUIMenu.h @@ -0,0 +1,46 @@ +#ifndef GUIMenu_h__ +#define GUIMenu_h__ + +#include "GUIPlane.h" +#include "GUIButton.h" +#include "GUIMenuItem.h" +#include + +namespace LifeGUI +{ + class GUIMenu : public GUIPlane + { + public: + EVENT1(MenuEvent, GUIMenu*); + EVENT2(MenuItemEvent, GUIMenu*, int); + MenuEvent Event_Collapse; + MenuItemEvent Event_SelectItem; + GUIMenu(bool allowFocus); + ~GUIMenu(); + void RemoveItems(); + GUIMenuItem* AddItem(const String& text, bool hasSubmenu); + private: + void OnCollapse(); + GUIMenu* parentMenu; + bool IsChildMenu(GUIWindow* m); + void SetParentMenu(GUIMenu* p); + GUIMenu* FindRootMenu(); + void OnLostFocus(GUIWindow* wnd, int arg); + void CollapseSubmenu(); + int expand_id; + size_t GetItemID(GUIMenuItem* itm); + void OnExpandMenu(GUIWindow* wnd, int arg); + void OnHoverElement(GUIWindow* wnd, int arg); + void OnSelectElement(GUIWindow* wnd, int arg); + void OnResize(GUIWindow* wnd); + void RebuildControls(); + typedef std::vector ItemCollection; + typedef std::vector::iterator ItemCollectionItr; + ItemCollection Items; + void UpdateLayout(); + WindowList internal_windows; + WindowList internal_arrows; + }; +} + +#endif // GUIMenu_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIMenuItem.cpp b/LifeGUI/src/GUIMenuItem.cpp new file mode 100644 index 0000000..e4cde21 --- /dev/null +++ b/LifeGUI/src/GUIMenuItem.cpp @@ -0,0 +1,26 @@ +#include "GUIMenuItem.h" +#include "GUIMenu.h" + +namespace LifeGUI +{ + GUIMenuItem::GUIMenuItem(const String& _text, bool hasSubmenu) + :SubMenu(0) + { + text = _text; + if(hasSubmenu) + { + SubMenu = new GUIMenu(false); + } + } + GUIMenuItem::~GUIMenuItem() + { + } + bool GUIMenuItem::HasSubmenu() + { + return SubMenu != 0; + } + GUIMenu* GUIMenuItem::GetSubmenu() + { + return SubMenu; + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUIMenuItem.h b/LifeGUI/src/GUIMenuItem.h new file mode 100644 index 0000000..3ac4d0b --- /dev/null +++ b/LifeGUI/src/GUIMenuItem.h @@ -0,0 +1,26 @@ +#ifndef GUIMenuItem_h__ +#define GUIMenuItem_h__ +#include "GUIWindow.h" +#include + +namespace LifeGUI +{ + class GUIMenu; + class GUIMenuItem + { + public: + EVENT1(MenuItemEvent, GUIMenuItem*); + MenuItemEvent Event_Select; + GUIMenuItem(const String& _text, bool hasSubmenu); + ~GUIMenuItem(); + bool HasSubmenu(); + GUIMenu* GetSubmenu(); + const String& GetText(){return text;} + protected: + GUIMenu* SubMenu; + String text; + GUIWindow* control; + }; +} + +#endif // GUIMenuItem_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIMenuTextItem.h b/LifeGUI/src/GUIMenuTextItem.h new file mode 100644 index 0000000..89da75f --- /dev/null +++ b/LifeGUI/src/GUIMenuTextItem.h @@ -0,0 +1,16 @@ +#ifndef GUIMenuTextItem_h__ +#define GUIMenuTextItem_h__ + +#include "GUIMenuItem.h" +namespace LifeGUI +{ + class GUIMenuTextItem : public GUIMenuItem + { + public: + GUIMenuTextItem(); + ~GUIMenuTextItem(); + bool IsDropDown(); + }; +} + +#endif // GUIMenuTextItem_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIMessaging.h b/LifeGUI/src/GUIMessaging.h new file mode 100644 index 0000000..33ab087 --- /dev/null +++ b/LifeGUI/src/GUIMessaging.h @@ -0,0 +1,59 @@ +#ifndef GUIMessaging_h__ +#define GUIMessaging_h__ +#include +#include "IInputSystem.h" + +namespace LifeGUI +{ + class GUIManager; + class GUIOverlay; + + enum GUIMessageType + { + GM_MOUSE = 0, + GM_KEYBOARD = 1, + GM_JOYSTICK = 2, + }; + + enum GUIMessageParam + { + MP_KEY_DOWN = 0, + MP_KEY_UP = 1, + MP_MOUSE_MOVE = 2 + }; + + class IMessageArgs + { + public: + virtual ~IMessageArgs() + { + } + }; + class IMouseArgs : public IMessageArgs + { + public: + ~IMouseArgs() + { + } + int ShiftRelX; + int ShiftRelY; + int CursorX; + int CursorY; + GUIManager* manager; + GUIOverlay* overlay; + int Mousekey; + }; + class IKeyboardArgs : public IMessageArgs + { + public: + ~IKeyboardArgs() + { + } + LifeCore::KeyCodes::Key keycode; + uint32 text; + std::string str; + GUIManager* manager; + GUIOverlay* overlay; + }; +} +#endif // GUIMessaging_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIOverlay.cpp b/LifeGUI/src/GUIOverlay.cpp new file mode 100644 index 0000000..42025fa --- /dev/null +++ b/LifeGUI/src/GUIOverlay.cpp @@ -0,0 +1,330 @@ +#include "GUIOverlay.h" +#include "GUIManager.h" + +namespace LifeGUI +{ + GUIOverlay::GUIOverlay(const String& _name) + :drawBackground(false), cursorPos(0,0), + hideCursor(false), focus(0), focusWindow(0), grabbed_wnd(0), grabbed_mouse(false), drag_mode(false), drag_info(0), + drag_src(0), drag_target(0), drop_valid(false), wnd_hover(0), wnd_hover_client(true) + { + cursorAbsolute = true; + cursor_relative.Set(0, 0); + name = _name; + GUIManager* manager = GUIManager::me(); + + this->SetPosition(LifeMath::Vec2i(0, 0)); + this->SetWidth(manager->GetWidth()); + this->SetHeight(manager->GetHeight()); + manager->GetRenderer()->SetScreenSize(GetWidth(), GetHeight()); + cursorGeom = new GUIPlane(); + cursorGeom->SetBorderEnabled(false); + cursorGeom->SetWidth(32); + cursorGeom->SetHeight(32); + + //load cursors + GUIWidgetSkin* cursor_skin = manager->skinDefault[STR("Cursor")]; + int cnt = cursor_skin->states.size(); + + for(std::map::iterator it = cursor_skin->states.begin(); it != cursor_skin->states.end(); ++it) + { + IGUICursor* cur = new IGUICursor(); + cur->skin = cursor_skin; + cur->name = it->first; + + LifeMath::Vec4i v = it->second->GetElement(STR("Background"))->Coord; + + int w = v.Z - v.X + 1; + int h = v.W - v.Y + 1; + + cur->size.X = w; + cur->size.Y = h; + + Cursors[cur->name] = cur; + } + + Cursors[STR("Size_h")]->offset = LifeMath::Vec2i(4, 11); + Cursors[STR("Size_v")]->offset = LifeMath::Vec2i(11, 4); + Cursors[STR("Text")]->offset = LifeMath::Vec2i(5, 10); + Cursors[STR("Size_d1")]->offset = LifeMath::Vec2i(8, 8); + Cursors[STR("Size_d2")]->offset = LifeMath::Vec2i(8, 8); + SetCursor("Arrow"); + } + void GUIOverlay::SetCursorMode(bool absolute){ + cursorAbsolute = absolute; + } + void GUIOverlay::GrabMouse(GUIWindow* wnd) + { + grabbed_mouse = true; + grabbed_wnd = wnd; + } + void GUIOverlay::BeginDrag(GUIWindow* source, DragInfo* info) + { + drag_mode = true; + drag_src = source; + drag_info = info; + drag_target = 0; + drop_valid = false; + } + + //release mouse after grabbing it + void GUIOverlay::ReleaseMouse() + { + grabbed_mouse = false; + grabbed_wnd = 0; + } + void GUIOverlay::OnMousePress(int x, int y, LifeCore::KeyCodes::Key key) + { + GUIWindow* wnd; + + if(grabbed_mouse) + { + wnd = grabbed_wnd; + wnd_hover_client = true; + } + else + wnd = GetWindowAtPoint(cursorPos.X, cursorPos.Y); + + GUIWindow* new_focus = wnd; + //find first window, which wants to accept keyboard focus + while(new_focus != 0 && !new_focus->NeedKeyFocus) + new_focus = new_focus->GetParent(); + //set new focus window + SetFocusWindow(new_focus); + + if(wnd && (wnd != this)) + { + if(wnd_hover_client) + wnd->OnMousePress(cursorPos.X, cursorPos.Y, key); + else + wnd->OnNCMousePress(cursorPos.X, cursorPos.Y, key); + } + } + LifeMath::Vec2i GUIOverlay::GetCursorCoord(){ + return cursorPos; + } + void GUIOverlay::OnMouseRelease(int x, int y, LifeCore::KeyCodes::Key key) + { + if(drag_mode) + { + if(drop_valid) + { + drag_target->AcceptDrop(drag_info); + drag_src->FinishDrag(); + } + } + else + { + GUIWindow* wnd; + if(grabbed_mouse) + { + wnd = grabbed_wnd; + wnd_hover_client = true; + } + else + wnd = GetWindowAtPoint(cursorPos.X, cursorPos.Y); + + if(wnd && (wnd != this)) + { + if(wnd_hover_client) + wnd->OnMouseRelease(cursorPos.X, cursorPos.Y, key); + else + wnd->OnNCMouseRelease(cursorPos.X, cursorPos.Y, key); + } + } + drag_mode = false; + } + + LifeMath::Vec2i GUIOverlay::GetCursorRelative(){ + return cursor_relative; + } + + void GUIOverlay::OnMouseMove(int abs_x, int abs_y, int rel_x, int rel_y) + { + if(cursorAbsolute){ + cursorPos.X = abs_x; + cursorPos.Y = abs_y; + }else{ + cursorPos.X += rel_x; + cursorPos.Y += rel_y; + } + + + + cursor_relative.Set(rel_x, rel_y); + + if(!drag_mode) + { + if(grabbed_mouse) + { + wnd_hover = grabbed_wnd; + wnd_hover_client = true; + } + else + { + GUIWindow* wnd = GetWindowAtPoint(cursorPos.X, cursorPos.Y); + bool clientZone = wnd != 0 ? wnd->GetClientRectangleAbs().InRect(cursorPos) : true; + //if changed window + if(wnd != wnd_hover) + { + //if there was hovered window, send it "mouse leave" message + if(wnd_hover) + { + if(wnd_hover_client) + wnd_hover->OnMouseLeave(); + else + wnd_hover->OnNCMouseLeave(); + } + //send "mouse enter" to newly hovered window + if(wnd) + { + wnd_hover_client = clientZone; + if(wnd_hover_client) + wnd->OnMouseEnter(); + else + wnd->OnNCMouseEnter(); + } + //change cursor + if((wnd != 0) && cursorGeom->state != wnd->cursor) + SetCursor(wnd->cursor); + } + else if(clientZone != wnd_hover_client) //changed only zone type + { + if(wnd) + { + if(clientZone)//if entered to client zone + { + wnd->OnNCMouseLeave(); + wnd->OnMouseEnter(); + } + else + { + wnd->OnMouseLeave(); + wnd->OnNCMouseEnter(); + } + wnd_hover_client = clientZone; + } + } + wnd_hover = wnd; + } + + if(wnd_hover && (wnd_hover != this)) + { + if(wnd_hover_client) + wnd_hover->OnMouseMove(cursorPos.X, cursorPos.Y, rel_x, rel_y); + else + wnd_hover->OnNCMouseMove(cursorPos.X, cursorPos.Y, rel_x, rel_y); + } + } + else //DragDrop + { + GUIWindow* target = GetWindowAtPoint(cursorPos.X, cursorPos.Y); + //mouse under different target + if(target != 0 && target != drag_target && target != drag_src) + { + drag_target = target; + if(target->IsAllowDrop() && target->DragEnter(drag_info)) + { + drop_valid = true; + } + else + { + drop_valid = false; + } + } + } + } + void GUIOverlay::OnKeyDown(LifeCore::KeyCodes::Key key, unsigned int text, const std::string& str){ + if(focusWindow){ + focusWindow->OnKeyDown(key, text, str); + } + } + void GUIOverlay::OnKeyUp(LifeCore::KeyCodes::Key key, unsigned int text, const std::string& str){ + if(focusWindow){ + focusWindow->OnKeyUp(key, text, str); + } + } + GUIOverlay::~GUIOverlay() + { + } + GUIOverlay* GUIOverlay::FindOverlay() + { + return this; + } + void GUIOverlay::SetBackColor(const LifeMath::float4& color) + { + BackColor = color; + } + void GUIOverlay::Update(float dt) + { + // UpdateChilds(dt); + } + void GUIOverlay::SetCursor(const String& name) + { + IGUICursor* cur = Cursors[name]; + + cursorGeom->state = cur->name; + cursorGeom->SetStyle(STR("Cursor")); + + cursorGeom->SetWidth(cur->size.X); + cursorGeom->SetHeight(cur->size.Y); + + cursor = cur; + } + /*bool GUIOverlay::MouseMove( int absX, int relX, int absY, int relY) + { + cursorPos += LifeMath::Vec2i(relX, relY); + + if(cursorPos.X < 0) + cursorPos.X = 0; + if(cursorPos.Y < 0) + cursorPos.Y = 0; + + if(cursorPos.X > (this->width - 5)) + cursorPos.X = (this->width - 5); + + if(cursorPos.Y > (this->height - 5)) + cursorPos.Y = (this->height - 5); + + LifeMath::float4 pos; + pos.Z = this->cursorPos.X; + pos.W = this->cursorPos .Y; + pos.X = GUIManager::me()->GetWidth(); + pos.Y = GUIManager::me()->GetHeight(); + + cbVs->Update(&pos, sizeof(LifeMath::float4)); + return true; + }*/ + + /*LifeMath::Rect2i GUIOverlay::GetClientRectangle() + { + return LifeMath::Rect2i(0, 0, this->GetWidth(), this->GetHeight()); + } + LifeMath::Rect2i GUIOverlay::GetClientRectangleAbs() + { + return LifeMath::Rect2i(0,0, this->GetWidth(), this->GetHeight()); + }*/ + void GUIOverlay::Render() + { + this->UpdateScissor(); + GUIManager::me()->GetRenderer()->SetScissor(this->GetScissor()); + if(drawBackground) + { + GUIManager::me()->GetRenderer()->SetTexture(GUIManager::me()->DIFF, 0, false); + GUIVertex cursor_vertex[6]; + GUIManager::me()->GenQuad(0, this->GetScissor(), LifeMath::Vec2i(0,0), GetWidth(), GetHeight(), &cursor_vertex[0]); + GUIManager::me()->GetRenderer()->Write(&cursor_vertex[0], 6); + } + + RenderChilds(); + + GUIManager::me()->GetRenderer()->SetScissor(this->GetScissor()); + if(!hideCursor) + { + GUIManager::me()->GetRenderer()->SetTexture(GUIManager::me()->cursorTex, 0, false); + cursorGeom->SetPosition(cursorPos - cursor->offset); + cursorGeom->SetScissor(this->GetScissor()); + cursorGeom->Render(); + } + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUIOverlay.h b/LifeGUI/src/GUIOverlay.h new file mode 100644 index 0000000..f45c726 --- /dev/null +++ b/LifeGUI/src/GUIOverlay.h @@ -0,0 +1,71 @@ +#ifndef GUIOverlay_h__ +#define GUIOverlay_h__ + +#include "GUIWindow.h" +#include "IInputSystem.h" +#include "delegate/Delegate.h" +#include "IGUICursor.h" +#include "GUIPlane.h" +#include +namespace LifeGUI +{ + class GUIOverlay : public GUIWindow{ + public: + EVENT1(GUIEvent, GUIWindow*); + LifeMath::Vec2i cursorPos; + GUIEvent FocusChanged; + bool drawBackground; + GUIWindow* focus; + + GUIOverlay(const String& _name); + ~GUIOverlay(); + GUIOverlay* FindOverlay(); + void SetCursorMode(bool absolute); + void SetCursor(const String& name); + void GrabMouse(GUIWindow* wnd); + void ReleaseMouse(); + void BeginDrag(GUIWindow* source, DragInfo* info); + GUIWindow* GetFocusWindow(){ + return focusWindow; + } + void SetFocusWindow(GUIWindow* wnd){ + GUIWindow* tmp = focusWindow; + focusWindow = wnd; + if(tmp != NULL && tmp != wnd) + tmp->ReleaseFocus(); + } + bool IsFocusWindow(GUIWindow* wnd){ + return focusWindow == wnd; + } + GUIWindow* focusWindow; + void Update(float dt); + //void KeyPress(int key, int modifier); + void Render(); + void SetBackColor(const LifeMath::float4& color); + + void OnMousePress(int x, int y, LifeCore::KeyCodes::Key key); + void OnMouseRelease(int x, int y, LifeCore::KeyCodes::Key key); + void OnMouseMove(int abs_x, int abs_y, int rel_x, int rel_y); + void OnKeyDown(LifeCore::KeyCodes::Key key, unsigned int text, const std::string& str); + void OnKeyUp(LifeCore::KeyCodes::Key key, unsigned int text, const std::string& str); + LifeMath::Vec2i GetCursorCoord(); + LifeMath::Vec2i GetCursorRelative(); + private: + bool cursorAbsolute; + LifeMath::Vec2i cursor_relative; + bool wnd_hover_client; + GUIPlane* cursorGeom; + GUIWindow* wnd_hover; + bool hideCursor; + bool grabbed_mouse; + GUIWindow* grabbed_wnd; + DragInfo* drag_info; + GUIWindow* drag_src; + GUIWindow* drag_target; + bool drop_valid; + bool drag_mode; + IGUICursor* cursor; + std::map Cursors; + }; +} +#endif // GUIOverlay_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIOverlayManager.cpp b/LifeGUI/src/GUIOverlayManager.cpp new file mode 100644 index 0000000..72412af --- /dev/null +++ b/LifeGUI/src/GUIOverlayManager.cpp @@ -0,0 +1,77 @@ +#include "GUIOverlayManager.h" +#include "GUIManager.h" + +namespace LifeGUI +{ + GUIOverlayManager::GUIOverlayManager() :selected_ovrl(nullptr), + scr_width(100), scr_height(100), cur_y(0), cur_x(0) + { + + } + bool GUIOverlayManager::KeyDown( LifeCore::KeyCodes::Key key, unsigned int text, const String& str ){ + selected_ovrl->OnKeyDown(key, text, str); + return true; + } + bool GUIOverlayManager::KeyUp( LifeCore::KeyCodes::Key key, unsigned int text, const std::string& str ){ + selected_ovrl->OnKeyUp(key, text, str); + return true; + } + bool GUIOverlayManager::MouseMove( int absX, int relX, int absY, int relY){ + cur_x = absX; + cur_y = absY; + selected_ovrl->OnMouseMove(absX,absY, relX,relY); + return true; + } + bool GUIOverlayManager::MousePressed( LifeCore::KeyCodes::Key button){ + selected_ovrl->OnMousePress(cur_x, cur_y, button); + return true; + } + bool GUIOverlayManager::MouseReleased( LifeCore::KeyCodes::Key button){ + selected_ovrl->OnMouseRelease(cur_x, cur_y, button); + return true; + } + GUIOverlay* GUIOverlayManager::GetOverlay(){ + return selected_ovrl; + } + GUIOverlay* GUIOverlayManager::GetOverlay(const String& name, bool create){ + GUIOverlay* ovrl = FindOverlay(name); + if(ovrl == nullptr && create){ + ovrl = new GUIOverlay(name); + overlays[name] = ovrl; + } + return ovrl; + } + void GUIOverlayManager::SetSize(uint32 width, uint32 height){ + scr_width = width; + scr_height = height; + + if(selected_ovrl != nullptr){ + selected_ovrl->SetWidth(scr_width); + selected_ovrl->SetHeight(scr_height); + } + } + bool GUIOverlayManager::SwitchOverlay(const String& name){ + selected_ovrl = FindOverlay(name); + if(selected_ovrl != nullptr){ + selected_ovrl->SetWidth(scr_width); + selected_ovrl->SetHeight(scr_height); + } + return true; + } + GUIOverlay* GUIOverlayManager::FindOverlay(const String& name){ + OverlayCollectionIt it = overlays.find(name); + if(it != overlays.end()) + return it->second; + else + return nullptr; + } + void GUIOverlayManager::Render(){ + if(selected_ovrl!=nullptr){ + LifeGUI::GUIManager::me()->GetRenderer()->SetScreenSize(scr_width, scr_height); + selected_ovrl->Render(); + + LifeGUI::GUIManager::me()->GetRenderer()->Render(); + LifeGUI::GUIManager::me()->GetRenderer()->Reset(); + } + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUIOverlayManager.h b/LifeGUI/src/GUIOverlayManager.h new file mode 100644 index 0000000..a7c741a --- /dev/null +++ b/LifeGUI/src/GUIOverlayManager.h @@ -0,0 +1,38 @@ +#ifndef GUIOverlayManager_h__ +#define GUIOverlayManager_h__ + +#include +#include +#include +#include "GUIOverlay.h" +#include + +namespace LifeGUI +{ + class GUIOverlayManager + { + public: + GUIOverlayManager(); + bool KeyDown( LifeCore::KeyCodes::Key key, unsigned int text, const String& str ); + bool KeyUp( LifeCore::KeyCodes::Key key, unsigned int text, const std::string& str ); + bool MouseMove( int absX, int relX, int absY, int relY); + bool MousePressed( LifeCore::KeyCodes::Key button); + bool MouseReleased( LifeCore::KeyCodes::Key button); + GUIOverlay* GetOverlay(); + GUIOverlay* GetOverlay(const String& name, bool create = false); + void SetSize(uint32 width, uint32 height); + bool SwitchOverlay(const String& name); + void Render(); + private: + GUIOverlay* selected_ovrl; + typedef std::map OverlayCollection; + typedef std::map::iterator OverlayCollectionIt; + OverlayCollection overlays; + GUIOverlay* FindOverlay(const String& name); + uint32 scr_width; + uint32 scr_height; + uint32 cur_x, cur_y; + }; +} + +#endif // GUIOverlayManager_h__ diff --git a/LifeGUI/src/GUIPicture.cpp b/LifeGUI/src/GUIPicture.cpp new file mode 100644 index 0000000..bfeb93e --- /dev/null +++ b/LifeGUI/src/GUIPicture.cpp @@ -0,0 +1,135 @@ +#include "GUIPicture.h" +#include "GUIManager.h" + +namespace LifeGUI +{ + GUIPicture::GUIPicture() + :mode(SM_STRETCH), pic(0) + { + this->SetBackColor(LifeMath::float4(0.7f, 0.7f, 0.7f, 1.0f)); + pictureHolder = new GUIPlane(); + pictureHolder->skip_mouse = true; + this->AddChild(pictureHolder); + //pictureHolder->SetDock(DOCK_FILL); + pictureHolder->SetBorderEnabled(false); + this->Event_Resize += NewDelegate(this, &GUIPicture::UpdateLayout); + } + GUIPicture::~GUIPicture() + { + } + void GUIPicture::SetSizeMode(int _mode) + { + mode = _mode; + UpdateLayout(this); + } + void GUIPicture::UpdateLayout(GUIWindow* wnd) + { + LifeMath::Rect2i r = this->GetClientRectangle(); + + if(pic) + { + pictureHolder->Show(); + + switch(mode) + { + case SM_STRETCH: + { + pictureHolder->SetPosition(r.GetMin()); + pictureHolder->SetWidth(r.width); + pictureHolder->SetHeight(r.height); + } + break; + case SM_CENTER: + { + int pw = pic->GetWidth(); + int ph = pic->GetHeight(); + if(pictureHolder->IsBorderEnabled()) + { + pw += 2; + ph += 2; + } + + int offsetX = (-(pw - r.width)) / 2; + int offsetY = (-(ph - r.height)) / 2; + + pictureHolder->SetPosition(LifeMath::Vec2i(offsetX, offsetY)); + pictureHolder->SetWidth(pw); + pictureHolder->SetHeight(ph); + } + break; + case SM_ZOOM: + { + int pw = pic->GetWidth(); + int ph = pic->GetHeight(); + + if(pictureHolder->IsBorderEnabled()) + { + pw += 2; + ph += 2; + } + + int dw = abs(r.width - pw); + int dh = abs(r.height - ph); + + float aspect = float(pw) / float(ph); + + if(dh < dw) //vertical on borders + { + pictureHolder->SetHeight(r.height); + pictureHolder->SetWidth(int(float(r.height) * aspect)); + pictureHolder->SetPosition(LifeMath::Vec2i(dw/2, 0)); + } + else + { + pictureHolder->SetWidth(r.width); + pictureHolder->SetHeight(int(float(r.width) / aspect)); + pictureHolder->SetPosition(LifeMath::Vec2i(0, dh / 2)); + } + } + break; + case SM_FILL: + { + int pw = pic->GetWidth(); + int ph = pic->GetHeight(); + + if(pictureHolder->IsBorderEnabled()) + { + pw += 2; + ph += 2; + } + + int dw = abs(r.width - pw); + int dh = abs(r.height - ph); + + float aspect = float(pw) / float(ph); + + if(dh < dw) //vertical on borders + { + pictureHolder->SetWidth(r.width); + pictureHolder->SetHeight(int(float(r.width) / aspect)); + pictureHolder->SetPosition(LifeMath::Vec2i(0, dh / 2)); + } + else + { + pictureHolder->SetHeight(r.height); + pictureHolder->SetWidth(int(float(r.height) * aspect)); + pictureHolder->SetPosition(LifeMath::Vec2i(dw / 2, 0)); + } + } + break; + } + } + else + { + pictureHolder->Hide(); + } + } + void GUIPicture::SetPicture(IImage* picture) + { + pic = picture; + + pictureHolder->SetBackPicture(picture); + pictureHolder->SetBackColor(LifeMath::float4(1,1,1,1)); + UpdateLayout(this); + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUIPicture.h b/LifeGUI/src/GUIPicture.h new file mode 100644 index 0000000..1ab38f9 --- /dev/null +++ b/LifeGUI/src/GUIPicture.h @@ -0,0 +1,32 @@ +#ifndef GUIPicture_h__ +#define GUIPicture_h__ + +#include "GUIPlane.h" + +namespace LifeGUI +{ + enum SIZE_MODE + { + SM_FILL, + SM_CENTER, + SM_STRETCH, + SM_ZOOM + }; + + class GUIPicture : public GUIWindow + { + public: + GUIPicture(); + ~GUIPicture(); + IImage* GetPicture(){return pic;} + void SetPicture(IImage* picture); + + void SetSizeMode(int _mode); + private: + int mode; + GUIPlane* pictureHolder; + void UpdateLayout(GUIWindow* wnd); + IImage* pic; + }; +} +#endif // GUIPicture_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIPlane.cpp b/LifeGUI/src/GUIPlane.cpp new file mode 100644 index 0000000..a6ef1a1 --- /dev/null +++ b/LifeGUI/src/GUIPlane.cpp @@ -0,0 +1,181 @@ +#include "GUIPlane.h" +#include "GUIManager.h" + +namespace LifeGUI +{ + GUIPlane::GUIPlane() + :border_color(0.9f, 0.9f, 0.9f, 1.0f), vertex_count(0), invalid(false) + { + back_tex = GUIManager::me()->DIFF; + + geom = new GUIVertex[6 + 8 * 6]; //background + border + this->SetBackColor(LifeMath::float4(1.0f, 1.0f, 1.0f, 1.0f)); + GenGeom(); + } + GUIPlane::~GUIPlane() + { + } + void GUIPlane::Invalidate() + { + GUIWindow::Invalidate(); + invalid = true; + } + void GUIPlane::GenGeom() + { + vertex_count = 0; + if(this->GetScissor().GetSquare() != 0) + { + if(skin) + { + SetBackPicture(skin->texture); + } + + if(borderEnabled) + { + if(skin) + { + GUISkinElement* e = skin->GetState(state)->GetElement(STR("Background")); + LifeMath::float4 m = e->Mapping; + + if(AutoTiling) + { + LifeMath::Rect2i r = this->GetClientRectangle(); + + BackgroundTiling.X = float(r.width) / float(e->Coord.Z - e->Coord.X + 1); + BackgroundTiling.Y = float(r.height) / float(e->Coord.W - e->Coord.Y + 1); + } + + vertex_count += GUIManager::me()->GenQuad(0, this->GetScissor(), LifeMath::Vec2i(Border.Left, Border.Top) + positionAbs, this->width - (Border.Left + Border.Right),this->height - (Border.Top + Border.Bottom), geom, BackColor,m.X, + m.Y, m.Z, m.W, BackgroundTiling); + + vertex_count += GUIManager::me()->GenFrame(0, this->GetScissor(), this->positionAbs.X, this->positionAbs.Y, this->width, this->height, &Border, border_color, geom + vertex_count); + } + else + { + if(back_tex) + vertex_count += GUIManager::me()->GenQuad(0, this->GetScissor(), LifeMath::Vec2i(Border.Left, Border.Top) + positionAbs,this->width - (Border.Left + Border.Right),this->height - (Border.Top + Border.Bottom),geom, BackColor,0,0,1,1,BackgroundTiling); + else + vertex_count += GUIManager::me()->GenQuad(0, this->GetScissor(), LifeMath::Vec2i(Border.Left, Border.Top) + positionAbs,this->width - (Border.Left + Border.Right),this->height - (Border.Top + Border.Bottom),geom, BackColor,0,0,0,0,BackgroundTiling); + + vertex_count += GUIManager::me()->GenFrame(0, this->GetScissor(), this->positionAbs.X, this->positionAbs.Y, this->width, this->height, &Border, border_color, geom + vertex_count); + } + } + else + { + if(skin) + { + GUISkinElement* e = skin->GetState(state)->GetElement(STR("Background")); + LifeMath::float4 m = e->Mapping; + + if(AutoTiling) + { + LifeMath::Rect2i r = this->GetClientRectangle(); + + BackgroundTiling.X = float(r.width) / float(e->Coord.Z - e->Coord.X + 1); + BackgroundTiling.Y = float(r.height) / float(e->Coord.W - e->Coord.Y + 1); + } + + vertex_count = GUIManager::me()->GenQuad(0, this->GetScissor(), positionAbs,this->width,this->height,geom, BackColor, m.X, + m.Y, m.Z, m.W, BackgroundTiling); + } + else + { + if(back_tex) + vertex_count = GUIManager::me()->GenQuad(0, this->GetScissor(), positionAbs,this->width,this->height,geom, BackColor,0,0,1,1,BackgroundTiling); + else + vertex_count = GUIManager::me()->GenQuad(0, this->GetScissor(), positionAbs,this->width,this->height,geom, BackColor,0,0,0,0, BackgroundTiling); + } + } + } + + invalid = false; + } + void GUIPlane::SetBackPicture(IImage* pic) + { + if(pic == 0) + back_tex = GUIManager::me()->DIFF; + else + back_tex = pic; + + Invalidate(); + } + void GUIPlane::Update(float dt) + { + } + void GUIPlane::RenderSelf() + { + //printf("Scissor X=%i, Y=%i\n", scissor.x, scissor.y); + //GUIManager::me()->GetRenderer()->SetScissor(scissor); + + if(invalid) + GenGeom(); + + if(this->GetScissor().GetSquare() > 0) + { + GUIManager::me()->GetRenderer()->SetTexture(back_tex, 0, false); + GUIManager::me()->GetRenderer()->Write(geom, vertex_count); + } + } + void GUIPlane::ReleaseFocus() + { + Event_LostFocus.Invoke(this, 0); + } + void GUIPlane::SetBackColor(const LifeMath::float4& color) + { + GUIWindow::SetBackColor(color); + Invalidate(); + } + void GUIPlane::SetBorderColor(const LifeMath::float4& color) + { + border_color = color; + Invalidate(); + } + bool GUIPlane::ProcessMessage(GUIMessageType mt, GUIMessageParam mp, IMessageArgs* args) + { + /* if(BroadcastMessage(mt, mp, args)) + return true; + + if(mt == GM_MOUSE && mouseEnabled) + { + IMouseArgs* mArgs = static_cast(args); + LifeMath::Vec2i cursor = mArgs->overlay->cursorPos; + + //if cursor is in client area + if(GetClientRectangleAbs().InRect(cursor)) + { + if(mp == MP_MOUSE_MOVE) + { + //PickChild() + } + else if(mp == MP_KEY_DOWN) + { + } + } + }*/ + return false; + } + LifeMath::Rect2i GUIPlane::GetClientRectangle() + { + LifeMath::Vec2i pos = this->GetPosition(); + if(borderEnabled) + { + return LifeMath::Rect2i(Border.Left, Border.Top, width - (Border.Left + Border.Right), height - (Border.Top + Border.Bottom)); + } + else + { + return LifeMath::Rect2i(0, 0, width, height); + } + } + LifeMath::Rect2i GUIPlane::GetClientRectangleAbs() + { + LifeMath::Vec2i pos = this->GetPositionAbs(); + if(borderEnabled) + { + return LifeMath::Rect2i(pos.X + Border.Left, pos.Y + Border.Top, width - (Border.Left + Border.Right), height - (Border.Top + Border.Bottom)); + } + else + { + return LifeMath::Rect2i(pos.X, pos.Y, width, height); + } + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUIPlane.h b/LifeGUI/src/GUIPlane.h new file mode 100644 index 0000000..255a99e --- /dev/null +++ b/LifeGUI/src/GUIPlane.h @@ -0,0 +1,37 @@ +#ifndef GUIPlane_h__ +#define GUIPlane_h__ + +#include "GUIWindow.h" +#include + +namespace LifeGUI +{ + class GUIPlane : public GUIWindow + { + public: + GUIPlane(); + ~GUIPlane(); + + void Update(float dt); + void RenderSelf(); + void SetBackColor(const LifeMath::float4& color); + void SetBorderColor(const LifeMath::float4& color); + void ReleaseFocus(); + bool ProcessMessage(GUIMessageType mt, GUIMessageParam mp, IMessageArgs* args); + void SetBackPicture(IImage* pic); + + LifeMath::Rect2i GetClientRectangle(); + LifeMath::Rect2i GetClientRectangleAbs(); + + void Invalidate(); + + private: + bool invalid; + void GenGeom(); + LifeMath::float4 border_color; + GUIVertex* geom; + int vertex_count; + IImage* back_tex; + }; +} +#endif // GUIPlane_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIProgress.cpp b/LifeGUI/src/GUIProgress.cpp new file mode 100644 index 0000000..b9c5f26 --- /dev/null +++ b/LifeGUI/src/GUIProgress.cpp @@ -0,0 +1,65 @@ +#include "GUIProgress.h" + +namespace LifeGUI +{ + GUIProgress::GUIProgress() + :mValue(0.0f) + { + this->SetWidth(300); + this->SetHeight(15); + this->SetBorderEnabled(false); + this->SetStyle(STR("Progress")); + + value_bar = new GUIPlane(); + value_bar->AutoTiling = true; + this->AddChild(value_bar); + value_bar->SetBorderEnabled(false); + value_bar->SetStyle(STR("Progress_line")); + + UpdateLayout(); + this->Event_Resize += NewDelegate(this, &GUIProgress::OnResize); + } + GUIProgress::~GUIProgress() + { + } + void GUIProgress::SetValue(float val) + { + mValue = val; + if(mValue < 0.0f) + mValue = 0.0f; + + if(mValue > 1.0f) + mValue = 1.0f; + + UpdateLayout(); + } + float GUIProgress::GetValue() + { + return mValue; + } + void GUIProgress::OnResize(GUIWindow* wnd) + { + UpdateLayout(); + } + void GUIProgress::UpdateLayout() + { + if(this->GetHeight() != 15) + this->SetHeight(15); + + if(value_bar->GetHeight() != 12) + value_bar->SetHeight(12); + + LifeMath::Rect2i r = this->GetClientRectangle(); + + LifeMath::Vec2i pos; + int offset_hor = 2; + int max_path = r.width - 2*offset_hor; + + pos.Y = (r.height - value_bar->GetHeight()) / 2; + pos.X = offset_hor; + value_bar->SetPosition(pos); + + int length = int(float(max_path) * mValue); + value_bar->SetWidth(length); + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUIProgress.h b/LifeGUI/src/GUIProgress.h new file mode 100644 index 0000000..7e79f1a --- /dev/null +++ b/LifeGUI/src/GUIProgress.h @@ -0,0 +1,23 @@ +#ifndef GUIProgress_h__ +#define GUIProgress_h__ + +#include "GUIPlane.h" + +namespace LifeGUI +{ + class GUIProgress : public GUIPlane + { + public: + GUIProgress(); + ~GUIProgress(); + void SetValue(float val); + float GetValue(); + private: + void OnResize(GUIWindow* wnd); + void UpdateLayout(); + GUIPlane* value_bar; + float mValue; + }; +} + +#endif // GUIProgress_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIPropertyGrid.cpp b/LifeGUI/src/GUIPropertyGrid.cpp new file mode 100644 index 0000000..eb9246f --- /dev/null +++ b/LifeGUI/src/GUIPropertyGrid.cpp @@ -0,0 +1,150 @@ +#include "GUIPropertyGrid.h" +#include "GUIText.h" +#include "GUITextInput.h" + +namespace LifeGUI +{ + GUIPropertyGrid::GUIPropertyGrid() + :target(nullptr) + { + this->SetWidth(100); + this->SetHeight(100); + } + GUIPropertyGrid::~GUIPropertyGrid() + { + } + void GUIPropertyGrid::AddEditor(GUIPropertyEditor* editor) + { + Editors.push_back(editor); + } + GUIPropertyEditor* GUIPropertyGrid::GetEditor(const String& name) + { + for(int i = 0; i < Editors.size(); i++) + { + if(Editors[i]->GetName() == name) + return Editors[i]; + } + return 0; + } + + + LifeCore::IPropertyHolder* GUIPropertyGrid::GetTarget() + { + return target; + } + void GUIPropertyGrid::SetTarget(LifeCore::IPropertyHolder* obj) + { + target = obj; + + for(PropertyLineCollectionItr it = Properties.begin(); it != Properties.end(); ++it) + { + //TODO Fix child removal! Failed when not found! + this->RemoveChild((*it)->editor->hwnd); + this->RemoveChild((*it)->header->hwnd); + (*it)->property->Event_ValueChanged.Remove((*it)->ValueChangedDelegate); + //TODO detach property line from event + delete *it; + } + Properties.clear(); + + if(target != nullptr) + { + PropertyCollection* props = target->GetProperties(); + + for(int i = 0; i < props->size(); i++) + { + LifeCore::IProperty* p = (*props)[i]; + String name = STR("Unnamed property!"); + p->GetAttribute(STR("DisplayName"), name); + + GUITextInput* text = new GUITextInput(); + text->SetText(name); + text->Border = GUIBorder(1); + + text->SetForeColor(LifeMath::float4(0,0,0,1)); + text->SetBackColor(LifeMath::float4(0,1,0,1)); + text->SetBorderEnabled(true); + + PropertyGridLine* line = new PropertyGridLine(); + line->header = text; + line->property = p; + + GUITextInput* input = new GUITextInput(); + input->SetText(p->ToString()); + + input->Event_MouseClick += NewDelegate(this,&GUIPropertyGrid::OnValueMouseDown); + input->SetBorderColor(LifeMath::float4(0,0,0,1)); + + line->ValueChangedDelegate = NewDelegate(line, &PropertyGridLine::OnValueChanged); + p->Event_ValueChanged += line->ValueChangedDelegate; + line->editor = input; + + input->SetBackColor(LifeMath::float4(1,0,0,1)); + Properties.push_back(line); + + input->UserData = line; + + this->AddChild(text); + this->AddChild(input); + + String propEditor; + line->propEditor = 0; + if(p->GetAttribute(STR("ValueEditor"), propEditor) == SC_OK) + { + GUIPropertyEditor* edt = GetEditor(propEditor); + line->propEditor = edt; + } + else + { + input->Event_EnterPressed += NewDelegate(this, &GUIPropertyGrid::OnAcceptValue); + } + } + + Format(); + } + } + void GUIPropertyGrid::OnValueMouseDown(GUIWindow* wnd, int param) + { + PropertyGridLine* line = (PropertyGridLine*)wnd->UserData; + + if(line->propEditor) + { + line->propEditor->BeginEdit(line); + } + } + void GUIPropertyGrid::OnAcceptValue(GUITextInput* input) + { + PropertyGridLine* line = (PropertyGridLine*)input->UserData; + + line->property->FromString(input->GetText()); + } + void GUIPropertyGrid::Format() + { + int lineHeight = 24; + + int id = 0; + for(PropertyLineCollectionItr it = Properties.begin(); it != Properties.end(); ++it) + { + GUIWindow* wndProp = (*it)->header; + wndProp->SetPosition(LifeMath::Vec2i(0, lineHeight * id)); + wndProp->SetHeight(lineHeight); + wndProp->SetWidth(this->GetClientRectangle().width / 2); + + GUIWindow* wndEdit = (*it)->editor; + wndEdit->SetPosition(LifeMath::Vec2i(this->GetClientRectangle().width / 2, lineHeight * id)); + wndEdit->SetHeight(lineHeight); + wndEdit->SetWidth(this->GetClientRectangle().width / 2); + id++; + } + } + void GUIPropertyGrid::SetWidth(int val) + { + GUIPlane::SetWidth(val); + Format(); + } + void GUIPropertyGrid::SetHeight(int val) + { + GUIPlane::SetHeight(val); + Format(); + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUIPropertyGrid.h b/LifeGUI/src/GUIPropertyGrid.h new file mode 100644 index 0000000..fb21e59 --- /dev/null +++ b/LifeGUI/src/GUIPropertyGrid.h @@ -0,0 +1,134 @@ +#ifndef GUIPropertyGrid_h__ +#define GUIPropertyGrid_h__ + +#include "GUIText.h" +#include "GUIPlane.h" +#include "GUITextInput.h" +#include "IPropertyHolder.h" +#include "GUIFileBrowserDialog.h" +#include "GUIOverlay.h" +#include + +namespace LifeGUI +{ + struct PropertyGridLine; + + class GUIPropertyEditor + { + public: + EVENT1(PropertyEditorEvent, PropertyGridLine*); + PropertyEditorEvent Event_FinishEdit; + virtual String GetName() = 0; + virtual void BeginEdit(PropertyGridLine* line) = 0; + private: + void OnFinishEdit(PropertyGridLine* line) + { + Event_FinishEdit.Invoke(line); + } + }; + + struct PropertyGridLine + { + GUITextInput* header; + GUIWindow* editor; + LifeCore::IProperty* property; + GUIPropertyEditor* propEditor; + CDelegate* ValueChangedDelegate; + + PropertyGridLine() + { + propEditor = 0; + property = 0; + editor = 0; + header = 0; + ValueChangedDelegate = 0; + } + + void OnValueChanged() + { + if(!propEditor || propEditor->GetName() == STR("FilePath")) + { + if(editor) + ((GUITextInput*)editor)->SetText(property->ToString()); + } + } + }; + + class GUIPropertyGrid : public GUIPlane + { + public: + GUIPropertyGrid(); + ~GUIPropertyGrid(); + void SetTarget(LifeCore::IPropertyHolder* obj); + LifeCore::IPropertyHolder* GetTarget(); + void SetWidth(int val); + void SetHeight(int val); + void AddEditor(GUIPropertyEditor* editor); + GUIPropertyEditor* GetEditor(const String& name); + void OnValueMouseDown(GUIWindow* wnd, int param); + private: + + LifeCore::IPropertyHolder* target; + typedef std::vector PropertyLineCollection; + typedef std::vector::iterator PropertyLineCollectionItr; + + typedef std::vector PropertyEditorCollection; + typedef std::vector::iterator PropertyEditorCollectionItr; + + PropertyLineCollection Properties; + PropertyEditorCollection Editors; + void Format(); + + void OnAcceptValue(GUITextInput* input); + }; + + class GUIFilePathPropertyEditor: public GUIPropertyEditor + { + public: + GUIFilePathPropertyEditor(GUIPropertyGrid* _parent) + { + parent = _parent; + fd = 0; + } + String GetName() + { + return STR("FilePath"); + } + void BeginEdit(PropertyGridLine* line) + { + prop = line; + printf("Before delete\n"); + if(fd) + delete fd; + printf("After delete\n"); + fd = new GUIFileBrowserDialog(); + //fd->SetFileSystem( ); + fd->SetWidth(300); + fd->SetHeight(280); + parent->FindOverlay()->AddChild(fd); + fd->Event_SelectedFile += NewDelegate(this, &GUIFilePathPropertyEditor::OnFileSelected); + } + private: + + void OnFileSelected(GUIFileBrowserDialog* dlg) + { + printf("Begin OnFileSelected\n"); + + if((int)prop->property == 0xfdfdfdfd || + (int)prop->property == 0xfeeefeee) + { + printf("EPIC FAIL!"); + } + + prop->property->FromString(dlg->GetSelectedFile()); + printf("Begin RemoveChild\n"); + parent->FindOverlay()->RemoveChild(fd->hwnd); + printf("End RemoveChild\n"); + printf("End OnFileSelected\n"); + } + PropertyGridLine* prop; + GUIFileBrowserDialog* fd; + GUIPropertyGrid* parent; + }; +} +#endif // GUIPropertyGrid_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIRadioButton.cpp b/LifeGUI/src/GUIRadioButton.cpp new file mode 100644 index 0000000..3dc5ff0 --- /dev/null +++ b/LifeGUI/src/GUIRadioButton.cpp @@ -0,0 +1,111 @@ +#include "GUIRadioButton.h" + +namespace LifeGUI +{ + GUIRadioButton::GUIRadioButton() + :_checked(false) + { + box = new GUIPlane(); + //box->skin_name = STR("Radio:Unchecked:Back"); + box->SetStyle(STR("Radio")); + + box->SetWidth(19); + box->SetHeight(19); + this->AddChild(box); + + text = new GUIText(); + text->SetText(STR("RadioButton")); + text->SetPosition(LifeMath::Vec2i(box->GetWidth(), 0)); + text->SetAutoSize(true); + text->SetVAlign(VA_CENTER); + this->AddChild(text); + + box->SetBorderEnabled(false); + text->SetBorderEnabled(false); + + box->Event_MouseClick += NewDelegate(this, &GUIRadioButton::OnBoxClick); + + UpdateLayout(); + } + GUIRadioButton::~GUIRadioButton() + { + if(box) + delete box; + if(text) + delete text; + } + + void GUIRadioButton::UpdateLayout() + { + int h1, h2; + + // + // + + h1 = box->GetHeight(); + h2 = text->GetHeight(); + + int text_offset = 5; + + if(h1 == h2) + { + this->SetHeight(h1); + box->SetPosition(LifeMath::Vec2i(0,0)); + text->SetPosition(LifeMath::Vec2i(box->GetWidth() + text_offset, 0)); + } + else if(h1 > h2) + { + this->SetHeight(h1); + box->SetPosition(LifeMath::Vec2i(0,0)); + text->SetPosition(LifeMath::Vec2i(box->GetWidth() + text_offset, (h1 - h2) / 2)); + } + else + { + this->SetHeight(h2); + box->SetPosition(LifeMath::Vec2i(0,(h2 - h1) / 2)); + text->SetPosition(LifeMath::Vec2i(box->GetWidth() + text_offset, 0)); + } + this->SetWidth(box->GetWidth() + text->GetWidth() + text_offset); + } + void GUIRadioButton::OnBoxClick(GUIWindow* wnd, int v) + { + InternalSetChecked(!_checked); + } + void GUIRadioButton::InternalSetChecked(bool state) + { + if(_checked != state) + { + _checked = state; + + if(_checked) + { + box->state = "Pressed"; + //box->skin_name = STR("Radio:Checked:Back"); + } + else + { + box->state = "Normal"; + box->skin_name = STR("Radio:Unchecked:Back"); + } + + box->Invalidate(); + Event_CheckChanged.Invoke(this); + } + } + bool GUIRadioButton::IsChecked() + { + return _checked; + } + void GUIRadioButton::SetChecked(bool state) + { + InternalSetChecked(state); + } + void GUIRadioButton::SetText(const String& _text) + { + text->SetText(_text); + } + const String& GUIRadioButton::GetText() + { + return text->GetText(); + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUIRadioButton.h b/LifeGUI/src/GUIRadioButton.h new file mode 100644 index 0000000..d7bcc3f --- /dev/null +++ b/LifeGUI/src/GUIRadioButton.h @@ -0,0 +1,30 @@ +#ifndef GUIRadioButton_h__ +#define GUIRadioButton_h__ + +#include "GUIPlane.h" +#include "GUIText.h" + +namespace LifeGUI +{ + class GUIRadioButton : public GUIWindow + { + public: + EVENT1(CheckEvent, GUIRadioButton*); + + CheckEvent Event_CheckChanged; + GUIRadioButton(); + ~GUIRadioButton(); + bool IsChecked(); + void SetChecked(bool state); + void SetText(const String& _text); + const String& GetText(); + private: + void InternalSetChecked(bool state); + void OnBoxClick(GUIWindow* wnd, int v); + bool _checked; + void UpdateLayout(); + GUIPlane* box; + GUIText* text; + }; +} +#endif // GUIRadioButton_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIRenderWindow.cpp b/LifeGUI/src/GUIRenderWindow.cpp new file mode 100644 index 0000000..42857a3 --- /dev/null +++ b/LifeGUI/src/GUIRenderWindow.cpp @@ -0,0 +1,82 @@ +#include "GUIRenderWindow.h" +#include + +namespace LifeGUI +{ + GUIRenderWindow::GUIRenderWindow(){ + picture = new GUIPicture(); + picture->Event_Resize += NewDelegate(this, &LifeGUI::GUIRenderWindow::OnPictureResize); + picture->NeedKeyFocus = true; + input_provider = new GUIInputProvider(); + input_provider->SetWindow(picture); + } + void GUIRenderWindow::OnPictureResize(LifeGUI::GUIWindow* sender){ + Event_Resize.Invoke(this); + } + + GUIPicture* GUIRenderWindow::GetGUIWindow(){ + return picture; + } + void GUIRenderWindow::Update(){ + + } + void GUIRenderWindow::SetCaption(const String& caption){ + + } + int GUIRenderWindow::GetWindowHandle(){ + return -1; + } + int GUIRenderWindow::GetWidth(){ + return picture->GetWidth(); + } + int GUIRenderWindow::GetHeight(){ + return picture->GetHeight(); + } + void GUIRenderWindow::SetSize(int w, int h){ + + } + const String& GUIRenderWindow::GetCaption(){ + return "GUIRenderWindow"; + } + + bool GUIRenderWindow::IsFullScreen(){ + return false; + } + void GUIRenderWindow::SetFullScreen(bool state){ + + } + bool GUIRenderWindow::SwitchMode(bool fullscreen){ + return false; + } + void GUIRenderWindow::SetFullscreenMode(LifeCore::DisplayMode mode){ + + } + + void GUIRenderWindow::OnActivate(){ + + } + void GUIRenderWindow::OnDeactivate(){ + + } + void GUIRenderWindow::OnResize(){ + + } + bool GUIRenderWindow::IsActive(){ + return true; + } + bool GUIRenderWindow::IsModeSwitching(){ + return false; + } + void GUIRenderWindow::SetCursorVisible(bool state){ + + } + bool GUIRenderWindow::IsCursorVisible(){ + return true; + } + void GUIRenderWindow::CenterWindow(){ + + } + LifeCore::IInputSystem* GUIRenderWindow::GetInputSystem(){ + return input_provider; + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUIRenderWindow.h b/LifeGUI/src/GUIRenderWindow.h new file mode 100644 index 0000000..458b231 --- /dev/null +++ b/LifeGUI/src/GUIRenderWindow.h @@ -0,0 +1,46 @@ +#ifndef GUIRenderWindow_h__ +#define GUIRenderWindow_h__ + +#include "GUIPicture.h" +#include "IRenderWindow.h" +#include +#include +#include "GUIInputProvider.h" + +namespace LifeGUI +{ + class GUIRenderWindow : public LifeCore::IRenderWindow + { + public: + GUIRenderWindow(); + GUIPicture* GetGUIWindow(); + void Update(); + void SetCaption(const String& caption); + int GetWindowHandle(); + int GetWidth(); + int GetHeight(); + void SetSize(int w, int h); + const String& GetCaption(); + + bool IsFullScreen(); + void SetFullScreen(bool state); + bool SwitchMode(bool fullscreen); + void SetFullscreenMode(LifeCore::DisplayMode mode); + + void OnActivate(); + void OnDeactivate(); + void OnResize(); + bool IsActive(); + bool IsModeSwitching(); + void SetCursorVisible(bool state); + bool IsCursorVisible(); + void CenterWindow(); + LifeCore::IInputSystem* GetInputSystem(); + + private: + void OnPictureResize(LifeGUI::GUIWindow* sender); + GUIPicture* picture; + GUIInputProvider* input_provider; + }; +} +#endif // GUIRenderWindow_h__ diff --git a/LifeGUI/src/GUIScroll.cpp b/LifeGUI/src/GUIScroll.cpp new file mode 100644 index 0000000..c619886 --- /dev/null +++ b/LifeGUI/src/GUIScroll.cpp @@ -0,0 +1,310 @@ +#include "GUIScroll.h" +#include "GUIManager.h" +#include "GUIButton.h" +namespace LifeGUI +{ + GUIScroll::GUIScroll(bool _vertical) + :vertical(_vertical), sliding(false), content_ratio(1.0f), sc_size(10), scroll_range(100), scroll_value(0), + sc_min_size(10), thickness(14) + { + b1 = new GUIPlane(); + b2 = new GUIPlane(); + scroller = new GUIButton(); + scroller->SetCaption(STR("")); + + this->AddChild(b1); + this->AddChild(b2); + this->AddChild(scroller); + + scroller->Event_MouseDown += NewDelegate(this, &GUIScroll::BeginSliding); + scroller->Event_MouseUp += NewDelegate(this, &GUIScroll::EndSliding); + scroller->Event_MouseMove += NewDelegate(this, &GUIScroll::DoSliding); + + b1->Event_MouseDown += NewDelegate(this, &GUIScroll::OnMinClick); + b2->Event_MouseDown += NewDelegate(this, &GUIScroll::OnMaxClick); + + scroller->SetBorderColor(LifeMath::float4(0.3f, 0.3f, 0.3f, 1.0f)); + scroller->SetBorderEnabled(false); + scroller->SetPosition(LifeMath::Vec2i(0,0)); + this->SetBackColor(LifeMath::float4(1,1,1,1)); + this->SetBorderEnabled(false); + + b1->SetBorderEnabled(false); + b2->SetBorderEnabled(false); + + if(vertical) + { + scroller->SetStyle(STR("VScroll_slider")); + this->SetStyle(STR("VScroll")); + b1->SetStyle(STR("VScroll_up")); + b2->SetStyle(STR("VScroll_down")); + } + else + { + scroller->SetStyle(STR("HScroll_slider")); + this->SetStyle(STR("HScroll")); + b1->SetStyle(STR("HScroll_left")); + b2->SetStyle(STR("HScroll_right")); + } + + this->Event_Resize += NewDelegate(this, &GUIScroll::UpdateLayout); + if(vertical) + { + this->SetWidth(thickness); + this->SetHeight(200); + } + else + { + this->SetWidth(200); + this->SetHeight(thickness); + } + } + void GUIScroll::SetScrollRange(int range) + { + scroll_range = range; + if(scroll_value > range) + scroll_value = range; + UpdateLayout(this); + } + void GUIScroll::OnMinClick(GUIWindow* w, int p) + { + if(vertical) + { + LifeMath::Vec2i pos = scroller->GetPosition(); + pos.Y -=1; + scroller->SetPosition(pos); + ComputeValue(); + UpdateLayout(this); + Event_Scroll.Invoke(this, GetValue()); + } + else + { + LifeMath::Vec2i pos = scroller->GetPosition(); + pos.X -=1; + scroller->SetPosition(pos); + ComputeValue(); + UpdateLayout(this); + Event_Scroll.Invoke(this, GetValue()); + } + } + void GUIScroll::OnMaxClick(GUIWindow* w, int p) + { + } + GUIScroll::~GUIScroll() + { + } + void GUIScroll::BeginSliding(GUIWindow* w, int p) + { + this->FindOverlay()->GrabMouse(w); + LifeMath::Vec2i pos = FindOverlay()->cursorPos; + scroller_click = scroller->CursorToLocal(pos); + sliding = true; + } + void GUIScroll::EndSliding(GUIWindow* w, int p) + { + this->FindOverlay()->ReleaseMouse(); + sliding = false; + } + void GUIScroll::DoSliding(GUIWindow* w, int p) + { + if(sliding) + { + LifeMath::Vec2i pos = FindOverlay()->cursorPos; + pos = this->CursorToClient(pos) - scroller_click; + if(vertical) + { + if(pos.Y < button_size) + pos.Y = button_size; + if(pos.Y > (button_size + sc_max_path)) pos.Y = (button_size + sc_max_path); + + LifeMath::Vec2i cp = scroller->GetPosition(); + if(cp.Y != pos.Y) + { + cp.Y = pos.Y; + sc_offset = cp.Y; + scroller->SetPosition(cp); + ComputeValue(); + //invoke event + Event_Scroll.Invoke(this, GetValue()); + } + } + else + { + if(pos.X < button_size) pos.X = button_size; + if(pos.X > (button_size + sc_max_path)) pos.X = (button_size + sc_max_path); + + LifeMath::Vec2i cp = scroller->GetPosition(); + if(cp.X != pos.X) + { + cp.X = pos.X; + sc_offset = cp.X; + scroller->SetPosition(cp); + ComputeValue(); + //invoke event + Event_Scroll.Invoke(this, GetValue()); + } + } + } + } + void GUIScroll::ComputeValue() + { + int val; + if(vertical) + { + val = scroller->GetPosition().Y; + } + else + val = scroller->GetPosition().X; + + val-= button_size; + + if(scroll_range > sc_max_path) + { + float scale = float(scroll_range) / float(sc_max_path); + scroll_value = int(floorf(float(val) * scale + 0.5f)); + } + else + { + scroll_value = val; + } + } + int GUIScroll::GetValue() + { + return scroll_value; + } + void GUIScroll::UpdateLayout(GUIWindow* w) + { + LifeMath::Rect2i rect_container = GetClientRectangle(); + if(vertical) + { + button_size = rect_container.width; + //scrollable distance between buttons + inner_size = rect_container.height - (button_size * 2); + } + else + { + button_size = rect_container.height; + //scrollable distance between buttons + inner_size = rect_container.width - (button_size * 2); + } + + //compute scroller vertical size + sc_size = inner_size - scroll_range; + //fix up scroller size (if it is smaller than minimal allowed size) + if(sc_size < sc_min_size) sc_size = sc_min_size; + sc_max_path = inner_size - sc_size; + sc_offset = int((float(scroll_value) / float(scroll_range)) * float(sc_max_path)); + + b1->SetWidth(button_size); + b1->SetHeight(button_size); + b2->SetWidth(button_size); + b2->SetHeight(button_size); + + b1->SetPosition(LifeMath::Vec2i(0, 0)); + + if(vertical) + { + b2->SetPosition(LifeMath::Vec2i(0, inner_size + button_size)); + scroller->SetPosition(LifeMath::Vec2i(0, sc_offset + button_size)); + scroller->SetWidth(button_size); + scroller->SetHeight(sc_size); + } + else + { + b2->SetPosition(LifeMath::Vec2i(inner_size + button_size, 0)); + scroller->SetPosition(LifeMath::Vec2i(sc_offset + button_size, 0)); + scroller->SetWidth(sc_size); + scroller->SetHeight(button_size); + } + /*LifeMath::Rect2i r = GetClientRectangle(); + + int max_size; + if(vertical) + { + max_size = r.height - b1->GetHeight() - b2->GetHeight(); + sc_size = int((float)max_size / content_ratio); + if(sc_size < 10) sc_size = 10; + scroller->SetHeight(sc_size); + } + else + { + max_size = r.width - b1->GetWidth() - b2->GetWidth(); + sc_size = int((float)max_size / content_ratio); + if(sc_size < 10) sc_size = 10; + scroller->SetWidth(sc_size); + } + + if(vertical) + { + b1->SetWidth(GetClientRectangle().width - 1); + b2->SetWidth(GetClientRectangle().width - 1); + b1->SetHeight(GetClientRectangle().width - 1); + b2->SetHeight(GetClientRectangle().width - 1); + scroller->SetWidth(GetClientRectangle().width); + scroller->SetHeight(sc_size); + + //fix slider size + int max_track_size = this->GetClientRectangle().height - b1->GetHeight() - b2->GetHeight(); + int new_slider_size = max_track_size - scroll_range; + if(new_slider_size < sc_min_size) new_slider_size = sc_min_size; + + scroller->SetHeight(new_slider_size); + //fix up scroller pos + if((new_slider_size + scroller->GetPosition().Y) > max_track_size) + { + sc_offset = max_track_size - new_slider_size; + scroller->SetPosition(LifeMath::Vec2i(0, max_track_size - new_slider_size)); + // Event_Scroll.Invoke(this, GetValue()); + } + + sc_max_path = this->GetClientRectangle().height - b1->GetHeight() - b2->GetHeight() - scroller->GetHeight(); + sc_offset = b1->GetHeight(); + min_c = sc_offset; + max_c = sc_max_path + sc_offset; + + if(scroller->GetPosition().Y < sc_offset) + { + scroller->SetPosition(LifeMath::Vec2i(0,sc_offset)); + // Event_Scroll.Invoke(this, GetValue()); + } + + b1->SetPosition(LifeMath::Vec2i(0, 0)); + b2->SetPosition(LifeMath::Vec2i(0, GetClientRectangle().height - b2->GetHeight())); + } + else + { + b1->SetWidth(GetClientRectangle().height - 1); + b2->SetWidth(GetClientRectangle().height - 1); + b1->SetHeight(GetClientRectangle().height - 1); + b2->SetHeight(GetClientRectangle().height - 1); + scroller->SetWidth(sc_size); + scroller->SetHeight(GetClientRectangle().height); + + //fix slider size + int max_track_size = this->GetClientRectangle().width - b1->GetWidth() - b2->GetWidth(); + int new_slider_size = max_track_size - scroll_range; + if(new_slider_size < sc_min_size) new_slider_size = sc_min_size; + + scroller->SetWidth(new_slider_size); + //fix up scroller pos + if((new_slider_size + scroller->GetPosition().X) > max_track_size) + { + sc_offset = max_track_size - new_slider_size; + scroller->SetPosition(LifeMath::Vec2i(max_track_size - new_slider_size, 0)); + //Event_Scroll.Invoke(this, GetValue()); + } + + sc_max_path = this->GetClientRectangle().width - b1->GetWidth() - b2->GetWidth() - scroller->GetWidth(); + sc_offset = b1->GetWidth(); + min_c = sc_offset; + max_c = sc_max_path + sc_offset; + if(scroller->GetPosition().X < sc_offset) + { + scroller->SetPosition(LifeMath::Vec2i(sc_offset, 0)); + } + + b1->SetPosition(LifeMath::Vec2i(0, 0)); + b2->SetPosition(LifeMath::Vec2i(GetClientRectangle().width - b2->GetWidth(), 0)); + }*/ + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUIScroll.h b/LifeGUI/src/GUIScroll.h new file mode 100644 index 0000000..cbf995d --- /dev/null +++ b/LifeGUI/src/GUIScroll.h @@ -0,0 +1,55 @@ +#ifndef GUIScroll_h__ +#define GUIScroll_h__ + +#include "GUIPlane.h" +#include "GUIButton.h" +#include + +namespace LifeGUI +{ + class GUIScroll : public GUIPlane + { + public: + EVENT2(ScrollEvent, GUIScroll*, int); + ScrollEvent Event_Scroll; + + GUIScroll(bool _vertical); + ~GUIScroll(); + void SetScrollRange(int range); + int GetValue(); + int GetThickness() + { + return thickness; + } + private: + size_t ValToOffset(size_t val); + size_t OffsetToVal(size_t offset); + int thickness; + bool vertical; + void ComputeValue(); + void UpdateLayout(GUIWindow* w); + void BeginSliding(GUIWindow* w, int p); + void EndSliding(GUIWindow* w, int p); + void DoSliding(GUIWindow* w, int p); + void OnMinClick(GUIWindow* w, int p); + void OnMaxClick(GUIWindow* w, int p); + GUIPlane* b1; + GUIPlane* b2; + GUIButton* scroller; + int sc_max_path; + int sc_pos; + int sc_offset; + int inner_size; + int button_size; + int sc_size; + int sc_min_size; + bool sliding; + int min_c, max_c; + LifeMath::Vec2i scroller_click; + float content_ratio; + int scroll_range; + int scroll_value; + }; +} + +#endif // GUIScroll_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIScrollablePlane.cpp b/LifeGUI/src/GUIScrollablePlane.cpp new file mode 100644 index 0000000..ddad982 --- /dev/null +++ b/LifeGUI/src/GUIScrollablePlane.cpp @@ -0,0 +1,206 @@ +#include "GUIScrollablePlane.h" + +namespace LifeGUI +{ + GUIScrollablePlane::GUIScrollablePlane() + :hscroll_enable(true),vscroll_enable(true), invalid(false) + { + hscroll = new GUIScroll(false); + vscroll = new GUIScroll(true); + container = new GUIPlane(); + corner = new GUIPlane(); + corner->SetWidth(vscroll->GetWidth()); + corner->SetHeight(vscroll->GetWidth()); + corner->SetBackColor(LifeMath::float4(1.0f, 1.0f, 1.0f, 1.0f)); + container->SetBackColor(LifeMath::float4(1.0f, 0.0f, 0.0f, 0.0f)); + corner->SetStyle(STR("ScrollablePlane")); + this->SetStyle(STR("ScrollablePlane")); + this->SetBorderEnabled(true); + this->SetBorderColor(LifeMath::float4(0.0f, 0.0f, 0.0f, 1.0f)); + corner->SetBorderEnabled(false); + + GUIWindow::AddChild(hscroll); + GUIWindow::AddChild(vscroll); + GUIWindow::AddChild(container); + GUIWindow::AddChild(corner); + + vscroll->Event_Scroll += NewDelegate(this, &GUIScrollablePlane::OnScrollVertical); + hscroll->Event_Scroll += NewDelegate(this, &GUIScrollablePlane::OnScrollHorisontal); + + this->Event_Resize += NewDelegate(this, &GUIScrollablePlane::UpdateLayout); + container->SetBorderEnabled(false); + this->Border.SetSize(1); + UpdateLayout(this); + } + int GUIScrollablePlane::RemoveChild(int hwnd) + { + return container->RemoveChild(hwnd); + } + void GUIScrollablePlane::OnScrollVertical(GUIScroll* wnd, int param) + { + //Invalidate(); + UpdateLayout(wnd); + } + void GUIScrollablePlane::OnScrollHorisontal(GUIScroll* wnd, int param) + { + //Invalidate(); + UpdateLayout(wnd); + } + + void GUIScrollablePlane::RenderSelf() + { + LifeMath::Rect2i r = container->GetContentRectangle(); + + if(r!= content_rect || invalid) + { + content_rect = r; + UpdateLayout(this); + } + + GUIPlane::RenderSelf(); + } + GUIScrollablePlane::~GUIScrollablePlane() + { + } + void GUIScrollablePlane::AddChild(GUIWindow* val) + { + container->AddChild(val); + } + LifeMath::Rect2i GUIScrollablePlane::GetClientRectangle() + { + LifeMath::Vec2i pos = this->GetPosition(); + LifeMath::Rect2i rect; + if(borderEnabled) + { + rect = LifeMath::Rect2i(Border.Left, Border.Top, width - (Border.Left + Border.Right), height - (Border.Top + Border.Bottom)); + } + else + { + rect = LifeMath::Rect2i(0, 0, width, height); + } + + /* if(vscroll_enable) + rect.width -= vscroll->GetWidth(); + if(hscroll_enable) + rect.height -= vscroll->GetHeight();*/ + + return rect; + } + LifeMath::Rect2i GUIScrollablePlane::GetClientRectangleAbs() + { + LifeMath::Vec2i pos = this->GetPositionAbs(); + LifeMath::Rect2i rect; + if(borderEnabled) + { + rect = LifeMath::Rect2i(pos.X + Border.Left, pos.Y + Border.Top, width - (Border.Left + Border.Right), height - (Border.Top + Border.Bottom)); + } + else + { + rect = LifeMath::Rect2i(pos.X, pos.Y, width, height); + } + + /*if(vscroll_enable) + rect.width -= vscroll->GetWidth(); + if(hscroll_enable) + rect.height -= vscroll->GetHeight();*/ + + return rect; + } + void GUIScrollablePlane::UpdateLayout(GUIWindow* wnd) + { + LifeMath::Rect2i r = this->GetClientRectangle(); + + LifeMath::Rect2i rc = container->GetContentRectangle(); + + hscroll_enable = rc.GetMaxX() > r.width; + vscroll_enable = rc.GetMaxY() > r.height; + + int w = r.width; + int h = r.height; + + if(vscroll_enable) + w -= vscroll->GetWidth(); + + if(hscroll_enable) + h -= vscroll->GetWidth(); + + if(rc.GetMaxX() > r.width) + { + //hscroll->SetContentRatio(float(rc.GetMaxX()) / float(r.width)); + hscroll->SetScrollRange(rc.GetMaxX() - w); + hscroll_enable = true; + hscroll->Show(); + } + else + { + hscroll_enable = false; + hscroll->Hide(); + } + + if(rc.GetMaxY() > r.height) + { + //hscroll->SetContentRatio(float(rc.GetMaxY()) / float(r.height)); + vscroll->SetScrollRange(rc.GetMaxY() - h); + vscroll_enable = true; + vscroll->Show(); + } + else + { + vscroll_enable = false; + vscroll->Hide(); + } + + if(vscroll_enable && hscroll_enable) + { + corner->Show(); + corner->SetPosition(LifeMath::Vec2i(r.width - vscroll->GetWidth(), r.height - vscroll->GetWidth())); + } + else + { + corner->Hide(); + } + + container_size.X = w; + container_size.Y = h; + + if(hscroll_enable) + { + hscroll->SetWidth(w); + hscroll->SetHeight(hscroll->GetThickness()); + hscroll->SetPosition(LifeMath::Vec2i(0, r.height - hscroll->GetHeight())); + } + + if(vscroll_enable) + { + vscroll->SetHeight(h); + // vscroll->SetWidth(13); + vscroll->SetPosition(LifeMath::Vec2i(r.width - vscroll->GetWidth(), 0)); + } + + LifeMath::Vec2i new_pos; + LifeMath::Vec2i new_size(w, h); + + if(vscroll_enable) + { + new_pos.Y = -vscroll->GetValue(); + //container->SetPosition(pos); + new_size.Y = container_size.Y + vscroll->GetValue(); + //container->SetHeight(); + } + + if(hscroll_enable) + { + //LifeMath::Vec2i pos = container->GetPosition(); + new_pos.X = -hscroll->GetValue(); + //container->SetPosition(pos); + new_size.X = container_size.X + hscroll->GetValue(); + //container->SetWidth(container_size.X - container->GetPosition().X); + } + + container->SetWidth(new_size.X); + container->SetHeight(new_size.Y); + + //container->SetPosition(LifeMath::Vec2i(0, -200)); + container->SetPosition(new_pos); + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUIScrollablePlane.h b/LifeGUI/src/GUIScrollablePlane.h new file mode 100644 index 0000000..a88ee6e --- /dev/null +++ b/LifeGUI/src/GUIScrollablePlane.h @@ -0,0 +1,50 @@ +#ifndef GUIScrollablePlane_h__ +#define GUIScrollablePlane_h__ + +#include "GUIPlane.h" +#include "GUIScroll.h" + +namespace LifeGUI +{ + class GUIScrollablePlane : public GUIPlane + { + private: + bool hscroll_enable; + bool vscroll_enable; + GUIScroll* hscroll; + GUIScroll* vscroll; + GUIPlane* container; + GUIPlane* corner; + void UpdateLayout(GUIWindow* wnd); + void OnScrollVertical(GUIScroll* wnd, int param); + void OnScrollHorisontal(GUIScroll* wnd, int param); + bool invalid; + LifeMath::Vec2i container_size; + protected: + void RenderSelf(); + public: + void Invalidate() + { + GUIPlane::Invalidate(); + invalid = true; + } + GUIScrollablePlane(); + ~GUIScrollablePlane(); + void AddChild(GUIWindow* val); + int RemoveChild(int hwnd); + LifeMath::Rect2i content_rect; + LifeMath::Rect2i GetClientRectangle(); + LifeMath::Rect2i GetClientRectangleAbs(); + + GUIScroll* GetHScroll() + { + return hscroll; + } + GUIScroll* GetVScroll() + { + return vscroll; + } + }; +} + +#endif // GUIScrollablePlane_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUISlider.cpp b/LifeGUI/src/GUISlider.cpp new file mode 100644 index 0000000..7c358fd --- /dev/null +++ b/LifeGUI/src/GUISlider.cpp @@ -0,0 +1,223 @@ +#include "GUISlider.h" +#include "GUIOverlay.h" + +namespace LifeGUI +{ + GUISlider::GUISlider(bool isVertical) + :vertical(isVertical), slider_size(18), value_min(0.0f), value_max(1.0f), value_now(0.5f), + sliding(false) + { + if(vertical) + { + this->SetWidth(50); + this->SetHeight(300); + } + else + { + this->SetWidth(300); + this->SetHeight(50); + } + + track_left = new GUIPlane(); + track_right = new GUIPlane(); + slider = new GUIPlane(); + + this->AddChild(track_left); + this->AddChild(track_right); + this->AddChild(slider); + + track_right->SetBorderEnabled(false); + track_left->SetBorderEnabled(false); + slider->SetBorderEnabled(false); + + if(!vertical) + { + track_left->SetStyle(STR("HSlider_left")); + track_right->SetStyle(STR("HSlider_right")); + slider->SetStyle(STR("HSlider_slider")); + } + else + { + track_left->SetStyle(STR("VSlider_left")); + track_right->SetStyle(STR("VSlider_right")); + slider->SetStyle(STR("VSlider_slider")); + } + + slider->Event_MouseDown += NewDelegate(this, &GUISlider::OnBeginSlide); + slider->Event_MouseUp += NewDelegate(this, &GUISlider::OnEndSlide); + slider->Event_MouseMove += NewDelegate(this, &GUISlider::OnDoSlide); + + track_left->Event_MouseDown += NewDelegate(this, &GUISlider::OnDoClick); + track_right->Event_MouseDown += NewDelegate(this, &GUISlider::OnDoClick); + + this->skip_mouse = true; + this->mouseEnabled = true; + UpdateLayout(this); + + this->Event_Resize += NewDelegate(this, &GUISlider::UpdateLayout); + } + void GUISlider::OnBeginSlide(GUIWindow* w, int p) + { + sliding = true; + LifeMath::Rect2i r = slider->GetClientRectangleAbs(); + slide_offset = (r.GetMin() + (r.GetMax() - r.GetMin()) / 2) - FindOverlay()->cursorPos; + FindOverlay()->GrabMouse(w); + } + void GUISlider::OnEndSlide(GUIWindow* w, int p) + { + sliding = false; + FindOverlay()->ReleaseMouse(); + } + void GUISlider::OnDoClick(GUIWindow* w, int p) + { + SetValue(PointToValue(FindOverlay()->cursorPos)); + Event_MouseDown.Invoke(this, 0); + } + float GUISlider::PointToValue(const LifeMath::Vec2i& point) + { + if(!vertical) + { + LifeMath::Rect2i r = this->GetClientRectangleAbs(); + int minX = r.x + slider_size.X / 2; + int maxX = r.x + r.width - slider_size.X / 2; + int cursor_pos = point.X + slide_offset.X; + if(cursor_pos < minX) cursor_pos = minX; + if(cursor_pos > maxX) cursor_pos = maxX; + + float a = float(cursor_pos - minX) / float(maxX - minX); + return value_min * (1 - a) + value_max * a; + } + else + { + LifeMath::Rect2i r = this->GetClientRectangleAbs(); + int minY = r.y + slider_size.Y / 2; + int maxY = r.y + r.height - slider_size.Y / 2; + int cursor_pos = point.Y + slide_offset.Y; + if(cursor_pos < minY) cursor_pos = minY; + if(cursor_pos > maxY) cursor_pos = maxY; + + float a = float(cursor_pos - minY) / float(maxY - minY); + return value_min * (1 - a) + value_max * a; + } + } + void GUISlider::OnDoSlide(GUIWindow* w, int p) + { + if(sliding) + { + SetValue(PointToValue(FindOverlay()->cursorPos)); + } + } + GUISlider::~GUISlider() + { + } + void GUISlider::UpdateLayout(GUIWindow* w) + { + slider->SetWidth(slider_size.X); + slider->SetHeight(slider_size.Y); + + int track_size = 6; + + if(!vertical)//horizontal + { + int width = this->GetWidth() - slider_size.X; + float a = (value_now - value_min) / (value_max - value_min); + int offset = int(float(width) * a); + + LifeMath::Vec2i pos; + pos.Y = (this->GetHeight() - track_size) / 2; + pos.X = slider_size.X / 2; + + track_left->SetPosition(pos); + track_left->SetWidth(offset); + track_left->SetHeight(track_size); + + pos.X += offset; + + track_right->SetPosition(pos); + track_right->SetWidth(width - offset); + track_right->SetHeight(track_size); + + pos.Y += track_size / 2; + pos -= (slider_size / 2); + slider->SetPosition(pos); + } + else + { + int height = this->GetHeight() - slider_size.Y; + float a = (value_now - value_min) / (value_max - value_min); + int offset = int(float(height) * a); + + LifeMath::Vec2i pos; + pos.X = (this->GetWidth() - track_size) / 2; + pos.Y = slider_size.Y / 2; + + track_left->SetPosition(pos); + track_left->SetHeight(offset); + track_left->SetWidth(track_size); + + pos.Y += offset; + + track_right->SetPosition(pos); + track_right->SetHeight(height - offset); + track_right->SetWidth(track_size); + + pos.X += track_size / 2; + pos -= (slider_size / 2); + slider->SetPosition(pos); + } + } + void GUISlider::SetStartValue(float val) + { + if(val > value_max) + val = value_max; + + value_min = val; + + if(value_now < value_min) + SetValue(value_min); + } + void GUISlider::SetEndValue(float val) + { + if(val < value_min) + val = value_min; + + value_max = val; + + if(value_now > value_max) + SetValue(value_max); + } + void GUISlider::SetValue(float val) + { + if(value_now == val) return; + + value_now = val; + + if(value_now > value_max) + value_now = value_max; + + if(value_now < value_min) + value_now = value_min; + + UpdateLayout(this); + Event_ValueChanged.Invoke(this, value_now); + } + float GUISlider::GetValue() + { + return value_now; + } + void GUISlider::OnBeginSliding(GUIWindow* w, int p) + { + } + void GUISlider::OnEndSliding(GUIWindow* w, int p) + { + } + void GUISlider::OnDoSliding(GUIWindow* w, int p) + { + } + void GUISlider::OnTapRight(GUIWindow* w, int p) + { + } + void GUISlider::OnTapLeft(GUIWindow* w, int p) + { + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUISlider.h b/LifeGUI/src/GUISlider.h new file mode 100644 index 0000000..16e0297 --- /dev/null +++ b/LifeGUI/src/GUISlider.h @@ -0,0 +1,48 @@ +#ifndef GUISlider_h__ +#define GUISlider_h__ + +#include "GUIPlane.h" + +namespace LifeGUI +{ + class GUISlider : public GUIWindow + { + public: + EVENT2(SliderEvent, GUISlider*, float); + SliderEvent Event_ValueChanged; + + GUISlider(bool isVertical); + ~GUISlider(); + void SetStartValue(float val); + void SetEndValue(float val); + void SetValue(float val); + float GetValue(); + protected: + void OnBeginSliding(GUIWindow* w, int p); + void OnEndSliding(GUIWindow* w, int p); + void OnDoSliding(GUIWindow* w, int p); + void OnTapRight(GUIWindow* w, int p); + void OnTapLeft(GUIWindow* w, int p); + + void OnBeginSlide(GUIWindow* w, int p); + void OnEndSlide(GUIWindow* w, int p); + void OnDoSlide(GUIWindow* w, int p); + + void OnDoClick(GUIWindow* w, int p); + private: + float PointToValue(const LifeMath::Vec2i& point); + LifeMath::Vec2i slide_offset; + LifeMath::Vec2i slider_size; + void UpdateLayout(GUIWindow* w); + bool vertical; + bool sliding; + GUIPlane* track_left; + GUIPlane* track_right; + GUIPlane* slider; + float value_min; + float value_max; + float value_now; + }; +} + +#endif // GUISlider_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUISplitPanel.cpp b/LifeGUI/src/GUISplitPanel.cpp new file mode 100644 index 0000000..0af0d94 --- /dev/null +++ b/LifeGUI/src/GUISplitPanel.cpp @@ -0,0 +1,244 @@ +#include "GUISplitPanel.h" +#include "GUIManager.h" +#include "GUIPlane.h" + +namespace LifeGUI +{ + GUISplitPanel::GUISplitPanel() + :splitter_vertical(true), splitter_size(7), splitter_pos(100), + minPanelSize(30), size_mode(false), update_realtime(false) + { + width = 200; + height = 200; + + plane1 = new GUIPlane(); + plane2 = new GUIPlane(); + plane_split = new GUIPlane(); + plane1->SetBorderColor(LifeMath::float4(0,0,0,1)); + plane2->SetBorderColor(LifeMath::float4(0,0,0,1)); + plane_split->SetBorderColor(LifeMath::float4(0,0,0,1)); + plane_split->SetBackColor(LifeMath::float4(0.4f,0.4f,0.4f,1)); + + // plane_split->mouseEnabled = false; + plane_split->cursor = STR("Size_v"); + plane_split->Event_MouseDown += NewDelegate(this, &GUISplitPanel::OnBeginSlide); + plane_split->Event_MouseUp += NewDelegate(this, &GUISplitPanel::OnEndSlide); + plane_split->Event_MouseMove += NewDelegate(this, &GUISplitPanel::OnDoSlide); + + AddChild(plane1); + AddChild(plane2); + AddChild(plane_split); + + UpdatePanels(); + } + GUISplitPanel::~GUISplitPanel() + { + } + void GUISplitPanel::SetSplitterPosition(size_t pos) + { + splitter_pos = pos; + UpdatePanels(); + } + void GUISplitPanel::SetBorderEnabled(bool state) + { + plane1->SetBorderEnabled(state); + plane2->SetBorderEnabled(state); + this->Invalidate(); + } + void GUISplitPanel::UpdatePanels() + { + if(splitter_vertical) + { + if(splitter_pos < minPanelSize) + splitter_pos = minPanelSize; + + if((this->GetWidth() - splitter_pos - splitter_size) < minPanelSize) + splitter_pos = this->GetWidth() - minPanelSize - splitter_size; + + plane1->SetPosition(LifeMath::Vec2i(0,0)); + plane1->SetWidth(splitter_pos); + plane1->SetHeight(this->GetHeight()); + + plane2->SetPosition(LifeMath::Vec2i(splitter_pos + splitter_size, 0)); + plane2->SetWidth(this->GetWidth() - splitter_pos - splitter_size); + plane2->SetHeight(this->GetHeight()); + + plane_split->SetPosition(LifeMath::Vec2i(splitter_pos, 0)); + plane_split->SetWidth(splitter_size); + plane_split->SetHeight(this->GetHeight()); + } + else + { + if(splitter_pos < minPanelSize) + splitter_pos = minPanelSize; + + if((this->GetHeight() - splitter_pos - splitter_size) < minPanelSize) + splitter_pos = this->GetHeight() - minPanelSize - splitter_size; + + plane1->SetPosition(LifeMath::Vec2i(0,0)); + plane1->SetWidth(this->GetWidth()); + plane1->SetHeight(splitter_pos); + + plane2->SetPosition(LifeMath::Vec2i(0, splitter_pos + splitter_size)); + plane2->SetWidth(this->GetWidth()); + plane2->SetHeight(this->GetHeight() - splitter_pos - splitter_size); + + plane_split->SetPosition(LifeMath::Vec2i(0, splitter_pos)); + plane_split->SetWidth(this->GetWidth()); + plane_split->SetHeight(splitter_size); + } + } + GUIWindow* GUISplitPanel::GetContainer1() + { + return plane1; + } + GUIWindow* GUISplitPanel::GetContainer2() + { + return plane2; + } + void GUISplitPanel::SetWidth(int val) + { + width = val; + UpdatePanels(); + } + void GUISplitPanel::SetHeight(int val) + { + height = val; + UpdatePanels(); + } + void GUISplitPanel::Update(float dt) + { + } + void GUISplitPanel::SetBackColor(const LifeMath::float4& color) + { + } + void GUISplitPanel::SetSplitterHorisontal(bool state) + { + splitter_vertical = !state; + if(splitter_vertical) + plane_split->cursor = STR("Size_v"); + else + plane_split->cursor = STR("Size_h"); + + UpdatePanels(); + } + void GUISplitPanel::SetRealtimeResize(bool state) + { + update_realtime = state; + } + + void GUISplitPanel::OnBeginSlide(GUIWindow* wnd, int p) + { + //transform cursor coordinate to local space + FindOverlay()->GrabMouse(wnd); + size_mode = true; + + //a little bit different code for vertical and horizontal splitters + if(splitter_vertical) + click_offset = FindOverlay()->cursorPos.X - plane_split->GetPositionAbs().X; + else + click_offset = FindOverlay()->cursorPos.Y - plane_split->GetPositionAbs().Y; + } + void GUISplitPanel::OnEndSlide(GUIWindow* wnd, int p) + { + FindOverlay()->ReleaseMouse(); + size_mode = false; + UpdatePanels(); + } + void GUISplitPanel::OnDoSlide(GUIWindow* wnd, int p) + { + LifeMath::Vec2i pos = CursorToLocal(FindOverlay()->cursorPos); + + if(size_mode) + { + if(splitter_vertical) + { + splitter_pos = pos.X - click_offset; + } + else + { + splitter_pos = pos.Y - click_offset; + } + if(update_realtime) + UpdatePanels(); + } + } + bool GUISplitPanel::ProcessMessage(GUIMessageType mt, GUIMessageParam mp, IMessageArgs* args) + { + /*if(mt == GM_MOUSE) + { + if(mp == MP_KEY_DOWN) + { + IMouseArgs* a = static_cast(args); + + //transform cursor coordinate to local space + LifeMath::Vec2i pos = CursorToLocal(LifeMath::Vec2i(a->CursorX, a->CursorY)); + + //a little bit different code for vertical and horizontal splitters + if(splitter_vertical) + { + //test if click happened inside splitter + if(pos.X >= splitter_pos && pos.X < (splitter_pos + splitter_size)) + { + if(pos.Y >=0 && pos.Y < this->GetHeight()) + { + size_mode = true; + click_offset = pos.X - splitter_pos; + return true; + } + } + } + else + { + //test if click happened inside splitter + if(pos.Y >= splitter_pos && pos.Y < (splitter_pos + splitter_size)) + { + if(pos.X >=0 && pos.X < this->GetWidth()) + { + size_mode = true; + click_offset = pos.Y - splitter_pos; + return true; + } + } + } + + return BroadcastMessage(mt, mp, args); + } + else if(mp == MP_KEY_UP) + { + if(size_mode) + { + size_mode = false; + UpdatePanels(); + } + else + return BroadcastMessage(mt, mp, args); + } + else if(mp == MP_MOUSE_MOVE) + { + IMouseArgs* a = static_cast(args); + + //transform cursor coordinate to local space + LifeMath::Vec2i pos = CursorToLocal(LifeMath::Vec2i(a->CursorX, a->CursorY)); + + if(size_mode) + { + if(splitter_vertical) + { + splitter_pos = pos.X - click_offset; + } + else + { + splitter_pos = pos.Y - click_offset; + } + if(update_realtime) + UpdatePanels(); + return true; + } + else + return BroadcastMessage(mt, mp, args); + } + }*/ + return false; + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUISplitPanel.h b/LifeGUI/src/GUISplitPanel.h new file mode 100644 index 0000000..1a85b74 --- /dev/null +++ b/LifeGUI/src/GUISplitPanel.h @@ -0,0 +1,48 @@ +#ifndef GUISplitPanel_h__ +#define GUISplitPanel_h__ + +#include "GUIWindow.h" + +namespace LifeGUI +{ + class GUIPlane; + + class GUISplitPanel : public GUIWindow + { + public: + GUISplitPanel(); + ~GUISplitPanel(); + void SetWidth(int val); + void SetHeight(int val); + void Update(float dt); + void SetBackColor(const LifeMath::float4& color); + void SetSplitterHorisontal(bool state); + void SetRealtimeResize(bool state); + void SetSplitterPosition(size_t pos); + GUIWindow* GetContainer1(); + GUIWindow* GetContainer2(); + bool ProcessMessage(GUIMessageType mt, GUIMessageParam mp, IMessageArgs* args); + + void OnBeginSlide(GUIWindow* wnd, int p); + void OnEndSlide(GUIWindow* wnd, int p); + void OnDoSlide(GUIWindow* wnd, int p); + + void SetBorderEnabled(bool state); + + private: + LifeMath::Vec2i splitter_click; + void UpdatePanels(); + int splitter_size; + bool splitter_vertical; + int splitter_pos; + int minPanelSize; + GUIPlane* plane1; + GUIPlane* plane2; + GUIPlane* plane_split; + bool size_mode; + int click_offset; + bool update_realtime; + }; +} + +#endif // GUISplitPanel_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUITab.cpp b/LifeGUI/src/GUITab.cpp new file mode 100644 index 0000000..9d2f2fe --- /dev/null +++ b/LifeGUI/src/GUITab.cpp @@ -0,0 +1,100 @@ +#include "GUITab.h" +#include "GUIManager.h" + +namespace LifeGUI +{ + GUITab::GUITab() + :Align(ALIGN_TOP), activeTab(0), invalid(true) + { + } + GUITab::~GUITab() + { + } + void GUITab::TabClick(GUIWindow* sender, int arg) + { + if(activeTab != sender) + activeTab->Reset(); + + size_t count = pages.size(); + GUITabPage* p = reinterpret_cast(sender->UserData); + //sender->state = STR("Pressed"); + //sender->Invalidate(); + activeTab = reinterpret_cast(sender); + p->Show(); + p->Invalidate(); + + for(size_t i = 0; i < count; i++) + { + GUITabPage* tp = pages[i]; + if(tp != p) + { + tp->Hide(); + } + } + } + void GUITab::RenderSelf() + { + if(invalid) + { + switch(Align) + { + case ALIGN_TOP: + { + size_t tab_count = tabs.size(); + int tabs_offset_left = 10; + //setup TAB buttons and pages + for(size_t i = 0; i < tab_count; i++) + { + GUIButton* b = tabs[i]; + b->SetWidth(60); + b->SetHeight(25); + b->SetPosition(LifeMath::Vec2i(tabs_offset_left + i * 60, 0)); + + GUITabPage* p = pages[i]; + p->SetHeight(this->GetHeight() - 25); + p->SetWidth(this->GetWidth()); + p->SetPosition(LifeMath::Vec2i(0, 25)); + } + } + break; + } + + invalid = false; + } + } + void GUITab::AddTab(const String& name) + { + GUITabPage* page = new GUITabPage(); + this->AddChild(page); + pages.push_back(page); + if(pages.size() > 1) + page->Hide(); + + page->AutoTiling = true; + page->SetStyle(STR("Tab_page")); + page->SetBackColor(LifeMath::float4(1,1,1,1)); + //page->SetBorderColor(LifeMath::float4(0,0,0,1)); + page->SetBorderEnabled(true); + page->Invalidate(); + + //TODO add search of same name in existing tabs and do something o_O + GUIButton* tab = new GUIButton(); + tab->SetStyle(STR("Tab_button")); + tab->SetCheckable(true); + if(activeTab == 0) + { + activeTab = tab; + tab->state = STR("Hover"); + } + tab->Event_MouseDown += NewDelegate(this, &GUITab::TabClick); + tab->UserData = page; + + tab->SetBorderEnabled(false); + tab->SetForeColor(LifeMath::float4(1,1,1,1)); + this->AddChild(tab); + tab->SetCaption(name); + tabs.push_back(tab); + + Invalidate(); + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUITab.h b/LifeGUI/src/GUITab.h new file mode 100644 index 0000000..66517e3 --- /dev/null +++ b/LifeGUI/src/GUITab.h @@ -0,0 +1,40 @@ +#ifndef GUITab_h__ +#define GUITab_h__ + +#include "GUIWindow.h" +#include "GUITabPage.h" +#include "GUIButton.h" + +#include + +namespace LifeGUI +{ + class GUITab : public GUIWindow + { + public: + GUITab(); + ~GUITab(); + void AddTab(const String& name); + void RenderSelf(); + ALIGN_TYPE Align; + GUITabPage* GetPage() + { + return reinterpret_cast(activeTab->UserData); + } + void Invalidate() + { + GUIWindow::Invalidate(); + invalid = true; + } + private: + bool invalid; + GUIButton* activeTab; + void TabClick(GUIWindow* sender, int arg); + typedef std::vector PageCollection; + typedef std::vector ButtonCollection; + ButtonCollection tabs; + PageCollection pages; + }; +} + +#endif // GUITab_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUITabPage.h b/LifeGUI/src/GUITabPage.h new file mode 100644 index 0000000..e569a5f --- /dev/null +++ b/LifeGUI/src/GUITabPage.h @@ -0,0 +1,16 @@ +#ifndef GUITabPage_h__ +#define GUITabPage_h__ + +#include "GUIScrollablePlane.h" + +namespace LifeGUI +{ + class GUITabPage : public GUIScrollablePlane + { + public: + GUITabPage(){} + ~GUITabPage(){} + }; +} + +#endif // GUITabPage_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIText.cpp b/LifeGUI/src/GUIText.cpp new file mode 100644 index 0000000..17b7390 --- /dev/null +++ b/LifeGUI/src/GUIText.cpp @@ -0,0 +1,311 @@ +#include "GUIText.h" +#include "GUIManager.h" + +namespace LifeGUI +{ + GUIText::GUIText() + :invalid(false), charCount(0), AutoSize(false), WrapWords(false), halign(HA_LEFT), valign(VA_TOP), quadCount(1), bufSize(128) + { + vert = new GUIVertex[128*6]; + this->SetBorderEnabled(false); + this->SetForeColor(LifeMath::float4(0,0,0,1)); + this->SetBackColor(LifeMath::float4(1,0,0,0)); + this->SetWidth(100); + this->SetHeight(20); + + this->Invalidate(); + } + + void GUIText::SetBackColor(const LifeMath::float4& color) + { + GUIPlane::SetBackColor(color); + Invalidate(); + SetText(text); + } + void GUIText::SetHAlign(HALIGN align) + { + if(halign != align) + { + halign = align; + SetText(text); + } + } + bool GUIText::ProcessMessage(GUIMessageType mt, GUIMessageParam mp, IMessageArgs* args) + { + return false; + } + void GUIText::SetVAlign(VALIGN align) + { + if(valign != align) + { + valign = align; + SetText(text); + } + } + void GUIText::SetText(const String& _text) + { + LifeMath::Rect2i s = this->GetScissor(); + vertex_count = 0; + + text = _text; + + std::wstring source; + + MBSTWCS(text, source); + + WStringVector srcLines; + WStringVector dstLines; + + //split string to lines + SplitStringW(source, &srcLines); + + int lineCount = srcLines.size(); + int maxWidth = 0; + for(int i = 0; i < lineCount; i++) + { + int strSize = GUIManager::me()->font->MeasureString(srcLines[i]); + if(strSize > maxWidth) + maxWidth = strSize; + + if(!AutoSize) + { + if(strSize <= this->width) + { + dstLines.push_back(srcLines[i]); + } + else //not autosize and string is too long + { + if(WrapWords) + { + WStringVector words; + SplitStringW(srcLines[i], &words, L' '); + int wordCount = words.size(); + + //now we need to split too long words + + std::wstring newLine(L""); + std::wstring oldLine(L""); + + for(int j = 0; j < wordCount; j++) + { + newLine = oldLine + words[j] + std::wstring(L" "); + if(GUIManager::me()->font->MeasureString(newLine) > this->width) + { + dstLines.push_back(oldLine); + oldLine = words[j]; + newLine = std::wstring(L""); + } + else if(j == (wordCount - 1)) + { + dstLines.push_back(newLine); + } + else + oldLine = newLine; + } + } + else + { + WStringVector lines; + SplitLongString(srcLines[i], &lines, this->width); + int cnt = lines.size(); + for(int k = 0; k < cnt; k++) + { + dstLines.push_back(lines[k]); + } + } + } + } + else + { + dstLines.push_back(srcLines[i]); + } + } + + int charCnt = source.size() + 1 + 5; + if(bufSize < charCnt) + { + bufSize = charCnt; + delete[]vert; + vert = new GUIVertex[bufSize * 6]; + } + + //genGeom(false); + //generate quads + int newLineCount = dstLines.size(); + + //delete[] vert; + //vert = new GUIVertex[(source.size()+10) * 6]; + + int xOff = 0; + int yOff = 0; + int counter = 0; + + int vShift = 0; + + if(valign == VA_TOP) + vShift = 0; + else if(valign == VA_BOTTOM) + { + int textHeight = dstLines.size() * GUIManager::me()->font->stringHeight; + vShift = this->height - textHeight; + } + else //center + { + int textHeight = dstLines.size() * GUIManager::me()->font->stringHeight; + vShift = (this->height - textHeight)/2; + } + + for(int i = 0; i < newLineCount; i++) + { + yOff = i * GUIManager::me()->font->stringHeight + vShift; + + if(halign == HA_LEFT) + xOff = 0; + else if(halign == HA_RIGHT) + { + int stringWidth = GUIManager::me()->font->MeasureString(dstLines[i]); + xOff = this->width - stringWidth; + } + else //center + { + int stringWidth = GUIManager::me()->font->MeasureString(dstLines[i]); + xOff = (this->width - stringWidth) / 2; + } + + int charCount = dstLines[i].size(); + const wchar_t* ptr = dstLines[i].c_str(); + + for(int j = 0; j < charCount; j++) + { + if(ptr[j] != 0x0020) + { + GUIFontGlyph* g = GUIManager::me()->font->GetGlyph(ptr[j]); + + vertex_count += GUIManager::me()->GenQuad(1, s, LifeMath::Vec2i(xOff + g->leftOffset, yOff + g->topOffset) + GetPositionAbs(),g->Width, g->Height, (vert + vertex_count),g->U, g->V, g->TexW, g->TexH,ForeColor,ForeColor,ForeColor,ForeColor, LifeMath::float2(1,1)); + + xOff+=g->hor_advance; + counter++; + } + else + { + xOff+=GUIManager::me()->font->spaceSize; + } + } + } + + quadCount = counter + 1; + + //update geom buffer + /* LifeCore::IDataStream* stm = vb->Map(0, LF_Discard); + stm->Write(vert, sizeof(GUIVertex) * 6 * quadCount); + vb->Unmap();*/ + if(AutoSize) + { + this->height = dstLines.size() * GUIManager::me()->font->stringHeight; + + int max_size = 0; + for(int i = 0; i < dstLines.size(); i++) + { + int line_size = GUIManager::me()->font->MeasureString(dstLines[i]); + if(max_size < line_size) + max_size = line_size; + } + this->width = max_size; + } + Event_TextChanged.Invoke(this, 0); + } + void GUIText::GenGeom(bool updateVb) + { + this->SetText(text); + invalid = false; + } + void GUIText::SplitLongString(const std::wstring& text, WStringVector* container, int maxLength) + { + GUIFont* fnt = GUIManager::me()->font; + + int stringSize = text.size(); + const wchar_t* str = text.c_str(); + + int oldSize = 0; + int newSize = 0; + int oldPos = 0; + //wchar_t tmp[2]; + std::wstring wtmp; + for(int i = 0; i < stringSize; i++) + { + GUIFontGlyph* glyph = fnt->GetGlyph(str[i]); + newSize = oldSize + glyph->Width; + + if(newSize > maxLength) + { + newSize = 0; + oldSize = 0; + + std::wstring ws = text.substr(oldPos, i - oldPos); + oldPos = i; + + (*container).push_back(ws); + } + else if(i == (stringSize - 1)) + { + if(i-oldPos) + { + std::wstring ws = text.substr(oldPos, i - oldPos); + + (*container).push_back(ws); + } + + return; + } + else + oldSize = newSize; + } + } + GUIText::~GUIText() + { + } + void GUIText::Update(float dt) + { + } + void GUIText::KeyPress(int key, int modifier) + { + } + void GUIText::SetWidth(int val) + { + width = val; + SetText(text); + //Invalidate(); + } + + void GUIText::SetForeColor(LifeMath::float4 color) + { + GUIPlane::SetForeColor(color); + + SetText(text); + Invalidate(); + } + void GUIText::SetHeight(int val) + { + height = val; + //SetText(text); + //Invalidate(); + } + const String& GUIText::GetText() + { + return text; + } + void GUIText::RenderSelf() + { + GUIPlane::RenderSelf(); + IGUIRenderer* renderer = GUIManager::me()->GetRenderer(); + + if(invalid) + GenGeom(true); + + if(this->GetScissor().GetSquare() != 0) + { + renderer->SetTexture(0, GUIManager::me()->font->font_tex, false); + renderer->Write(vert, vertex_count); + } + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUIText.h b/LifeGUI/src/GUIText.h new file mode 100644 index 0000000..eee37d9 --- /dev/null +++ b/LifeGUI/src/GUIText.h @@ -0,0 +1,72 @@ +#ifndef GUIText_h__ +#define GUIText_h__ +#include "GUIWindow.h" +#include "GUIPlane.h" + +namespace LifeGUI +{ + enum HALIGN + { + HA_LEFT = 0, + HA_CENTER = 1, + HA_RIGHT = 2 + }; + enum VALIGN + { + VA_TOP = 0, + VA_CENTER = 1, + VA_BOTTOM = 2 + }; + class GUIText : public GUIPlane + { + private: + GUIVertex* vert; + + int charCount; + String text; + bool AutoSize; + bool invalidGeom; + bool WrapWords; + int quadCount; + void SplitLongString(const std::wstring& text, WStringVector* container, int maxLength); + void GenGeom(bool updateVb); + int halign; + int valign; + int bufSize; + int vertex_count; + bool invalid; + public: + EVENT2(TextEvent, GUIWindow*, uint32); + + TextEvent Event_TextChanged; + + void SetForeColor(LifeMath::float4 color); + void SetBackColor(const LifeMath::float4& color); + void SetHAlign(HALIGN align); + void SetVAlign(VALIGN align); + void Invalidate() + { + GUIPlane::Invalidate(); + invalid = true; + } + void SetAutoSize(bool condition) + { + AutoSize = condition; + } + bool GetAutoSize() + { + return AutoSize; + } + GUIText(); + ~GUIText(); + void Update(float dt); + void KeyPress(int key, int modifier); + void RenderSelf(); + void SetWidth(int val); + void SetHeight(int val); + void SetText(const String& _text); + const String& GetText(); + bool ProcessMessage(GUIMessageType mt, GUIMessageParam mp, IMessageArgs* args); + }; +} +#endif // GUIText_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUITextInput.cpp b/LifeGUI/src/GUITextInput.cpp new file mode 100644 index 0000000..bf03d42 --- /dev/null +++ b/LifeGUI/src/GUITextInput.cpp @@ -0,0 +1,153 @@ +#include "GUITextInput.h" +#include "GUIManager.h" + +namespace LifeGUI +{ + GUITextInput::GUITextInput() + { + NeedKeyFocus = true; + this->SetWidth(150); + this->SetHeight(20); + + caption = new GUIText(); + caption->SetWidth(this->GetWidth()); + caption->SetHeight(this->GetHeight()); + caption->SetText(STR("omfg")); + caption->SetVAlign(VA_CENTER); + caption->SetHAlign(HA_LEFT); + caption->SetPosition(LifeMath::Vec2i(0,0)); + caption->SetBackColor(LifeMath::float4(0,0,0,0)); + caption->SetForeColor(LifeMath::float4(0,0,0,1)); + caption->SetBorderEnabled(false); + this->SetBackColor(LifeMath::float4(0.9f,0.9f,0.9f,0.5f)); + this->AddChild(caption); + this->cursor = STR("Text"); + this->SetBorderColor(LifeMath::float4(0,0,0,1)); + this->SetBorderEnabled(true); + this->Border = GUIBorder(1); + caption->mouseEnabled = false; + this->Event_MouseDown += NewDelegate(this, &GUITextInput::OnMouseDown); + } + void GUITextInput::RenderSelf() + { + if(invalid) + { + LifeMath::Rect2i r = this->GetClientRectangle(); + caption->SetWidth(r.width); + caption->SetHeight(r.height); + } + GUIPlane::RenderSelf(); + } + void GUITextInput::SetForeColor(LifeMath::float4 color) + { + caption->SetForeColor(color); + } + void GUITextInput::OnKeyDown(LifeCore::KeyCodes::Key key, unsigned int text, const std::string& str) + { + switch(key) + { + case LifeCore::KeyCodes::Key_BACK: + { + String s = caption->GetText(); + if(s.size() > 0) + { + s = s.substr(0, s.size()-1); + caption->SetText(s); + } + } + break; + case LifeCore::KeyCodes::Key_TAB: + { + } + break; + case LifeCore::KeyCodes::Key_RETURN: + { + Event_EnterPressed.Invoke(this); + } + break; + default: + { + if(text != 0 && str != std::string("")) + caption->SetText(caption->GetText() + str); + } + break; + } + } + void GUITextInput::OnMouseDown(GUIWindow* wnd, int p) + { + FindOverlay()->SetFocusWindow(wnd); + } + + GUITextInput::~GUITextInput() + { + } + String GUITextInput::GetText() + { + return caption->GetText(); + } + void GUITextInput::SetText(const String& _text) + { + caption->SetText(_text); + } + /* + bool GUITextInput::ProcessMessage(GUIMessageType mt, GUIMessageParam mp, IMessageArgs* args) + { + //mouse message + if(mt == GM_MOUSE && mouseEnabled) + { + //get mouse args + IMouseArgs* mArgs = static_cast(args); + + LifeMath::Vec2i cursor = mArgs->overlay->cursorPos; + bool grabMouse = GetRectangleAbs().InRect(cursor); + + if(mp == MP_KEY_DOWN) + { + if(grabMouse) + { + FindOverlay()->focusWindow = this; + return true; + } + else + { + if(FindOverlay()->focusWindow == this) + { + FindOverlay()->focusWindow = 0; + } + } + } + } + else if(mt == GM_KEYBOARD && FindOverlay()->focusWindow == this) + { + IKeyboardArgs* kArgs = static_cast(args); + + if(mp == MP_KEY_DOWN && kArgs->keycode != KEY_LSHIFT && kArgs->keycode != KEY_BACK) + { + String str = text->GetText(); + #ifdef _UNICODE + str += String((wchar_t)kArgs->text); + #else + String dst; + + WCSTMBS(reinterpret_cast(&kArgs->text), dst); + str += dst; + //str+=kArgs->str; + #endif + text->SetText(str); + return true; + } + else if(mp == MP_KEY_DOWN && kArgs->keycode == KEY_BACK) + { + String str = text->GetText(); + int l = str.size(); + if(l <= 1) + text->SetText(STR("")); + else + text->SetText(str.substr(0, l-1)); + + return true; + } + } + return false; + }*/ +} \ No newline at end of file diff --git a/LifeGUI/src/GUITextInput.h b/LifeGUI/src/GUITextInput.h new file mode 100644 index 0000000..d97f03f --- /dev/null +++ b/LifeGUI/src/GUITextInput.h @@ -0,0 +1,34 @@ +#ifndef GUITextInput_h__ +#define GUITextInput_h__ + +#include "GUIPlane.h" +#include "GUIText.h" + +namespace LifeGUI +{ + class GUITextInput : public GUIPlane + { + public: + EVENT1(TextInputEvent, GUITextInput*); + TextInputEvent Event_EnterPressed; + + GUITextInput(); + ~GUITextInput(); + void SetText(const String& text); + String GetText(); + void SetForeColor(LifeMath::float4 color); + void RenderSelf(); + void GUITextInput::OnKeyDown(LifeCore::KeyCodes::Key key, unsigned int text, const std::string& str); + void Invalidate() + { + GUIPlane::Invalidate(); + invalid = true; + } + private: + bool invalid; + void OnMouseDown(GUIWindow* wnd, int p); + GUIText* caption; + }; +} + +#endif // GUITextInput_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIToolStrip.cpp b/LifeGUI/src/GUIToolStrip.cpp new file mode 100644 index 0000000..cd93bf6 --- /dev/null +++ b/LifeGUI/src/GUIToolStrip.cpp @@ -0,0 +1,63 @@ +#include "GUIToolStrip.h" + +namespace LifeGUI +{ + GUIToolStrip::GUIToolStrip() + :isVertical(false) + { + this->Event_Resize += NewDelegate(this, &GUIToolStrip::UpdateLayout); + this->SetBackColor(LifeMath::float4(0.737f, 0.7803f, 0.847f, 1.0f)); + this->SetBorderColor(LifeMath::float4(0.78823f, 0.82353f, 0.8824f, 1.0f)); + this->SetBorderEnabled(true); + } + GUIToolStrip::~GUIToolStrip() + { + } + void GUIToolStrip::AddItem(GUIToolStripItem* itm) + { + Items.push_back(itm); + AddChild(itm->GetInternalWindow()); + UpdateLayout(this); + } + void GUIToolStrip::SetIsVertical(bool state) + { + if(isVertical != state) + { + isVertical = state; + UpdateLayout(this); + } + } + void GUIToolStrip::UpdateLayout(GUIWindow* wnd) + { + if(isVertical) + { + } + else + { + int s_h = 24; + int i_h = 22; + + if (this->GetHeight() != s_h) + this->SetHeight(s_h); + + int spacing = 3; + + //arrange all childs + int itm_count = Items.size(); + int itm_w = 35; + int itm_h = 22; + + int off_y = (s_h - itm_h) / 2; + + for(int i = 0; i < itm_count; i++) + { + GUIToolStripItem* itm = Items[i]; + + GUIWindow* wnd = itm->GetInternalWindow(); + wnd->SetPosition(LifeMath::Vec2i((itm_w + spacing) * i + spacing, off_y)); + wnd->SetWidth(itm_w); + wnd->SetHeight(itm_h); + } + } + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUIToolStrip.h b/LifeGUI/src/GUIToolStrip.h new file mode 100644 index 0000000..0cc7d8d --- /dev/null +++ b/LifeGUI/src/GUIToolStrip.h @@ -0,0 +1,27 @@ +#ifndef GUIToolStrip_h__ +#define GUIToolStrip_h__ + +#include "GUIButton.h" +#include "GUIToolStripItem.h" +#include + +namespace LifeGUI +{ + class GUIToolStrip : public GUIPlane + { + public: + GUIToolStrip(); + ~GUIToolStrip(); + void AddItem(GUIToolStripItem* itm); + void SetIsVertical(bool state); + private: + void UpdateLayout(GUIWindow* wnd); + protected: + typedef std::vector ItemCollection; + typedef std::vector::iterator ItemCollectionIter; + ItemCollection Items; + bool isVertical; + }; +} + +#endif // GUIToolStrip_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIToolStripButton.cpp b/LifeGUI/src/GUIToolStripButton.cpp new file mode 100644 index 0000000..79b7b2c --- /dev/null +++ b/LifeGUI/src/GUIToolStripButton.cpp @@ -0,0 +1,20 @@ +#include "GUIToolStripButton.h" +#include "GUIButton.h" + +namespace LifeGUI +{ + GUIToolStripButton::GUIToolStripButton() + { + pWindow = new GUIButton(); + GUIButton* b = (GUIButton*)pWindow; + b->SetBorderEnabled(false); + b->SetCaption(STR("Tool")); + } + GUIToolStripButton::~GUIToolStripButton() + { + } + GUIButton* GUIToolStripButton::AsButton() + { + return static_cast(pWindow); + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUIToolStripButton.h b/LifeGUI/src/GUIToolStripButton.h new file mode 100644 index 0000000..e7131c8 --- /dev/null +++ b/LifeGUI/src/GUIToolStripButton.h @@ -0,0 +1,17 @@ +#ifndef GUIToolStripButton_h__ +#define GUIToolStripButton_h__ + +#include "GUIToolStripItem.h" +#include "GUIButton.h" + +namespace LifeGUI +{ + class GUIToolStripButton : public GUIToolStripItem + { + public: + GUIButton* AsButton(); + GUIToolStripButton(); + ~GUIToolStripButton(); + }; +} +#endif // GUIToolStripButton_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIToolStripImage.cpp b/LifeGUI/src/GUIToolStripImage.cpp new file mode 100644 index 0000000..23ab2e6 --- /dev/null +++ b/LifeGUI/src/GUIToolStripImage.cpp @@ -0,0 +1,15 @@ +#include "GUIToolStripImage.h" +#include "GUIPicture.h" + +namespace LifeGUI +{ + GUIToolStripImage::GUIToolStripImage() + { + pWindow = new GUIPicture(); + GUIPicture* b = (GUIPicture*)pWindow; + b->SetSizeMode(SM_CENTER); + } + GUIToolStripImage::~GUIToolStripImage() + { + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUIToolStripImage.h b/LifeGUI/src/GUIToolStripImage.h new file mode 100644 index 0000000..54e9349 --- /dev/null +++ b/LifeGUI/src/GUIToolStripImage.h @@ -0,0 +1,15 @@ +#ifndef GUIToolStripImage_h__ +#define GUIToolStripImage_h__ + +#include "GUIToolStripItem.h" + +namespace LifeGUI +{ + class GUIToolStripImage : public GUIToolStripItem + { + public: + GUIToolStripImage(); + ~GUIToolStripImage(); + }; +} +#endif // GUIToolStripImage_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIToolStripItem.cpp b/LifeGUI/src/GUIToolStripItem.cpp new file mode 100644 index 0000000..e69de29 diff --git a/LifeGUI/src/GUIToolStripItem.h b/LifeGUI/src/GUIToolStripItem.h new file mode 100644 index 0000000..799911f --- /dev/null +++ b/LifeGUI/src/GUIToolStripItem.h @@ -0,0 +1,17 @@ +#ifndef GUIToolStripItem_h__ +#define GUIToolStripItem_h__ + +#include "GUIWindow.h" + +namespace LifeGUI +{ + class GUIToolStripItem + { + public: + GUIWindow* GetInternalWindow() { return pWindow; }; + protected: + GUIWindow* pWindow; + }; +} + +#endif // GUIToolStripItem_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIToolStripMenu.cpp b/LifeGUI/src/GUIToolStripMenu.cpp new file mode 100644 index 0000000..1b91b93 --- /dev/null +++ b/LifeGUI/src/GUIToolStripMenu.cpp @@ -0,0 +1,60 @@ +#include "GUIToolStripMenu.h" +#include "GUIButton.h" +#include "GUIOverlay.h" + +namespace LifeGUI +{ + GUIToolStripMenu::GUIToolStripMenu() + :menu_visible(false), show_on_click(true) + { + pWindow = new GUIButton(); + GUIButton* b = (GUIButton*)pWindow; + b->SetBorderEnabled(false); + b->SetCaption(STR("Tool")); + b->Event_MouseDown += NewDelegate(this, &GUIToolStripMenu::OnButtonPress); + + menu = new GUIMenu(true); + menu->Event_Collapse += NewDelegate(this, &GUIToolStripMenu::OnMenuHide); + menu->Event_SelectItem += NewDelegate(this, &GUIToolStripMenu::OnMenuItemSelect); + } + void GUIToolStripMenu::SetCaption(const String& text) + { + GUIButton* b = (GUIButton*)pWindow; + b->SetCaption(text); + } + void GUIToolStripMenu::OnMenuItemSelect(GUIMenu* wnd, int p) + { + show_on_click = true; + } + GUIToolStripMenu::~GUIToolStripMenu() + { + } + void GUIToolStripMenu::OnButtonPress(GUIWindow* wnd, int p) + { + if(menu_visible) + { + pWindow->FindOverlay()->ReleaseFocus(); + menu_visible = false; + } + else + { + if(show_on_click) + { + pWindow->FindOverlay()->AddChild(menu); + pWindow->FindOverlay()->SetFocusWindow(menu); + LifeMath::Vec2i newPos = pWindow->GetRectangleAbs().GetMax(); + newPos.X-= pWindow->GetWidth(); + menu->SetPosition(newPos); + + menu_visible = true; + } + show_on_click = true; + } + } + void GUIToolStripMenu::OnMenuHide(GUIMenu* wnd) + { + menu_visible = false; + if(pWindow->GetClientRectangleAbs().InRect(pWindow->FindOverlay()->cursorPos)) + show_on_click = false; + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUIToolStripMenu.h b/LifeGUI/src/GUIToolStripMenu.h new file mode 100644 index 0000000..a61ed3f --- /dev/null +++ b/LifeGUI/src/GUIToolStripMenu.h @@ -0,0 +1,26 @@ +#ifndef GUIToolStripMenu_h__ +#define GUIToolStripMenu_h__ + +#include "GUIToolStripItem.h" +#include "GUIMenu.h" + +namespace LifeGUI +{ + class GUIToolStripMenu : public GUIToolStripItem + { + public: + GUIToolStripMenu(); + ~GUIToolStripMenu(); + GUIMenu* GetMenu(){return menu;} + void SetCaption(const String& text); + private: + bool show_on_click; + bool menu_visible; + GUIMenu* menu; + void OnButtonPress(GUIWindow* wnd, int p); + void OnMenuItemSelect(GUIMenu* wnd, int p); + void OnMenuHide(GUIMenu* wnd); + }; +} + +#endif // GUIToolStripMenu_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUITreeNode.cpp b/LifeGUI/src/GUITreeNode.cpp new file mode 100644 index 0000000..1660942 --- /dev/null +++ b/LifeGUI/src/GUITreeNode.cpp @@ -0,0 +1,19 @@ +#include "GUITreeNode.h" +#include "GUITreeView.h" + +namespace LifeGUI +{ + void GUITreeNode::SetOwner(GUITreeView* _owner) + { + owner = _owner; + } + void GUITreeNode::OnArrowClick(GUIWindow* sender, int args) + { + if(IsCollapsed()) + { + Expand(); + } + else + Collapse(); + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUITreeNode.h b/LifeGUI/src/GUITreeNode.h new file mode 100644 index 0000000..9d69447 --- /dev/null +++ b/LifeGUI/src/GUITreeNode.h @@ -0,0 +1,82 @@ +#ifndef GUITreeNode_h__ +#define GUITreeNode_h__ + +#include "LString.h" +#include +#include "GUIPlane.h" +#include "GUIText.h" + +namespace LifeGUI +{ + class GUITreeView; + class GUITreeNode + { + public: + bool need_update; + + GUITreeView* owner; + void SetOwner(GUITreeView* _owner); + GUITreeNode() + :collapsed(true), need_update(true) + { + groupButton = new GUIPlane(); + text = new GUIText(); + text->SetText(STR("Tree node")); + groupButton->Hide(); + groupButton->SetStyle(STR("Tree_button")); + groupButton->Event_MouseDown += NewDelegate(this, &GUITreeNode::OnArrowClick); + } + void AddChildNode(GUITreeNode* node) + { + children.push_back(node); + } + String Name; + size_t ChildrenCount() + { + return children.size(); + } + GUITreeNode& operator[](size_t index) + { + return *children[index]; + } + void Collapse() + { + collapsed = true; + groupButton->state = "Normal"; + need_update = true; + groupButton->Invalidate(); + } + GUITreeNode* GetChild(size_t id) + { + return children[id]; + } + void Expand() + { + collapsed = false; + groupButton->state = "Pressed"; + need_update = true; + groupButton->Invalidate(); + } + bool IsCollapsed() + { + return collapsed; + } + GUIPlane* GetGroupButton() + { + return groupButton; + } + GUIText* GetLabel() + { + return text; + } + private: + void OnArrowClick(GUIWindow* sender, int args); + typedef std::vector NodeCollection; + typedef std::vector::iterator NodeCollectionIter; + GUIPlane* groupButton; + GUIText* text; + bool collapsed; + NodeCollection children; + }; +} +#endif // GUITreeNode_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUITreeView.cpp b/LifeGUI/src/GUITreeView.cpp new file mode 100644 index 0000000..729242b --- /dev/null +++ b/LifeGUI/src/GUITreeView.cpp @@ -0,0 +1,127 @@ +#include "GUITreeView.h" +#include "GUIManager.h" + +namespace LifeGUI +{ + GUITreeView::GUITreeView() + { + } + GUITreeView::~GUITreeView() + { + } + void GUITreeView::RenderSelf() + { + for(NodeCollectionIter it = nodes.begin(); it != nodes.end(); ++it) + { + GUITreeNode* node = (*it); + if(NeedUpdate(node)) + { + Invalidate(); + //UpdateLayout(); + break; + } + } + + if(invalid) + UpdateLayout(); + + GUIScrollablePlane::RenderSelf(); + } + void GUITreeView::AddNode(GUITreeNode* node) + { + nodes.push_back(node); + node->SetOwner(this); + // this->AddChild(node->GetLabel()); + // this->AddChild(node->GetGroupButton()); + Invalidate(); + } + void GUITreeView::UpdateLayout() + { + //childs.clear(); + + //clear child nodes + for(std::vector::iterator it = internal_childs.begin(); it != internal_childs.end(); ++it) + { + GUIWindow* wnd = *it; + this->RemoveChild(wnd->hwnd); + } + internal_childs.clear(); + + last_node = 0; + for(NodeCollectionIter it = nodes.begin(); it != nodes.end(); ++it) + { + GUITreeNode* node = (*it); + ExposeNode(node, 0); + } + Invalidate(); + } + bool GUITreeView::NeedUpdate(GUITreeNode* node) + { + if(node->need_update) + return true; + else + { + size_t cnt = node->ChildrenCount(); + if(cnt && !node->IsCollapsed()) + for(size_t i = 0; i < cnt; i++) + { + if(NeedUpdate(node->GetChild(i))) + return true; + } + } + return false; + } + void GUITreeView::ExposeNode(GUITreeNode* node, int depth) + { + int x_main_off = 25; + int y_main_off = 25; + + int node_height = 15; + int left_offset = 20; + node->need_update = false; + if(node->ChildrenCount() > 0) + { + int y_off = node_height * last_node + x_main_off; + int x_off = left_offset * depth + y_main_off; + + GUIText* label = node->GetLabel(); + label->SetPosition(LifeMath::Vec2i(x_off, y_off)); + this->AddChild(label); + internal_childs.push_back(label); + + GUIPlane* button = node->GetGroupButton(); + button->SetWidth(14); + button->SetHeight(14); + button->SetBorderEnabled(false); + // button->SetBorderColor(LifeMath::float4(0,0,0,1)); + + button->SetPosition(LifeMath::Vec2i(x_off - 18, y_off + 4)); + this->AddChild(button); + internal_childs.push_back(button); + button->Show(); + + last_node++; + + if(!node->IsCollapsed()) + { + size_t node_cnt = node->ChildrenCount(); + for(size_t i = 0; i < node_cnt; i++) + { + GUITreeNode* n = node->GetChild(i); + ExposeNode( n, depth + 1); + } + } + } + else + { + int y_off = node_height * last_node + x_main_off; + int x_off = left_offset * depth + y_main_off; + + GUIText* label = node->GetLabel(); + label->SetPosition(LifeMath::Vec2i(x_off, y_off)); + this->AddChild(label); + internal_childs.push_back(label); + last_node++; + } + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUITreeView.h b/LifeGUI/src/GUITreeView.h new file mode 100644 index 0000000..f70331e --- /dev/null +++ b/LifeGUI/src/GUITreeView.h @@ -0,0 +1,42 @@ +#ifndef GUITreeView_h__ +#define GUITreeView_h__ + +#include "GUIScrollablePlane.h" +#include "GUITreeNode.h" +#include "GUIButton.h" +#include "GUIText.h" + +namespace LifeGUI +{ + struct GroupButton + { + GUIButton* b; + bool grouped; + }; + class GUITreeView : public GUIScrollablePlane + { + public: + GUITreeView(); + ~GUITreeView(); + void AddNode(GUITreeNode* node); + void Invalidate() + { + GUIScrollablePlane::Invalidate(); + invalid = true; + } + protected: + void RenderSelf(); + private: + bool invalid; + bool NeedUpdate(GUITreeNode* node); + WindowList internal_childs; + typedef std::vector NodeCollection; + typedef std::vector::iterator NodeCollectionIter; + size_t last_node; + void UpdateLayout(); + void ExposeNode(GUITreeNode* node, int depth); + NodeCollection nodes; + }; +} + +#endif // GUITreeView_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIVertex.h b/LifeGUI/src/GUIVertex.h new file mode 100644 index 0000000..770d2ce --- /dev/null +++ b/LifeGUI/src/GUIVertex.h @@ -0,0 +1,64 @@ +#ifndef GUIVertex_h__ +#define GUIVertex_h__ + +#include +#include +#include +#include + +namespace LifeGUI +{ + struct RectangleInfo + { + LifeMath::float4 v1; + LifeMath::float4 v2; + }; + + struct GUIVertex + { + LifeMath::float3 Position; + LifeMath::float4 Color; + LifeMath::float4 Texcoord; + LifeMath::float4 TileInfo; + + GUIVertex() + :Position(0,0,0), Color(0,0,0,0), Texcoord(0,0, 1, 1) + { + } + + GUIVertex(LifeMath::float3 pos, LifeMath::float4 col, LifeMath::float4 tc, LifeMath::float4 tc_tile) + { + Position = pos; + Color = col; + Texcoord = tc; + TileInfo = tc_tile; + } + + const GUIVertex operator *(float val) const + { + GUIVertex result; + result.Color = this->Color * val; + result.Position = this->Position * val; + result.Texcoord = this->Texcoord * val; + return result; + } + + const GUIVertex operator +(const GUIVertex& val) const + { + GUIVertex result; + result.Color = this->Color + val.Color; + result.Position = this->Position + val.Position; + result.Texcoord = this->Texcoord + val.Texcoord; + return result; + } + + const GUIVertex& operator *=(float val) + { + this->Color *= val; + this->Position *= val; + this->Texcoord *= val; + return *this; + } + }; +} +#endif // GUIVertex_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIWidgetSkin.h b/LifeGUI/src/GUIWidgetSkin.h new file mode 100644 index 0000000..3255d15 --- /dev/null +++ b/LifeGUI/src/GUIWidgetSkin.h @@ -0,0 +1,96 @@ +#ifndef GUIWidgetSkin_h__ +#define GUIWidgetSkin_h__ + +#include "LString.h" +#include +#include "math/LifeMath.h" +#include "Types.h" + + +namespace LifeGUI +{ + class GUISkinElement; + class GUISkinState; + class GUIWidgetSkin; + + class GUISkinElement + { + public: + LifeMath::Vec4i Coord; + LifeMath::float4 Mapping; + GUISkinElement(const LifeMath::Vec4i& pixCoords, const LifeMath::float4& texCoords) + { + Coord = pixCoords; + Mapping = texCoords; + } + }; + class GUISkinState + { + private: + std::map elements; + LifeMath::Vec2i texSize; + LifeMath::float4 ComputeTC(const LifeMath::Vec4i& coord) + { + float x, y, z, w; + LifeMath::Vec4i pixCoord = coord + LifeMath::Vec4i(0,0,1,1); + + x = (float)pixCoord.X / (float)texSize.X; + y = (float)pixCoord.Y / (float)texSize.Y; + z = (float)pixCoord.Z / (float)texSize.X; + w = (float)pixCoord.W / (float)texSize.Y; + + return LifeMath::float4(x, y, z-x, w-y); + } + public: + GUISkinState(const LifeMath::Vec2i& texDim) + { + texSize = texDim; + } + ~GUISkinState() + { + } + void AddElement(const String& name, const LifeMath::Vec4i& data) + { + GUISkinElement* element = new GUISkinElement(data, ComputeTC(data)); + elements[name] = element; + } + GUISkinElement* GetElement(const String& name) + { + return elements[name]; + } + }; + class GUIWidgetSkin + { + public: + IImage* texture; + std::map states; + + GUIWidgetSkin(IImage* skinTex) + { + texture = skinTex; + } + GUISkinState* AddState(const String& name) + { + GUISkinState* state = new GUISkinState(LifeMath::Vec2i(texture->GetWidth(), texture->GetHeight())); + states[name] = state; + return state; + } + GUISkinState* GetState(const String& name) + { + if(states.find(name) == states.end()) + { + if(states.find(STR("Normal")) != states.end()) + { + return states[STR("Normal")]; + } + else + { + return 0; + } + } + else + return states[name]; + } + }; +} +#endif // GUIWidgetSkin_h__ \ No newline at end of file diff --git a/LifeGUI/src/GUIWindow.cpp b/LifeGUI/src/GUIWindow.cpp new file mode 100644 index 0000000..e56b0a4 --- /dev/null +++ b/LifeGUI/src/GUIWindow.cpp @@ -0,0 +1,91 @@ +#include "GUIWindow.h" +#include "GUIManager.h" + +namespace LifeGUI +{ + static int wndCounter = 0; + GUIWindow::GUIWindow() + :parent(0), position(0, 0), width(100), height(100), positionAbs(0, 0), BackColor(1.0f, 1.0f, 1.0f, 1.0f), visible(true), + borderEnabled(true), mouseCaptured(false), enabled(true), mouseEnabled(true), mouseButtons(0), + dock(DOCK_NONE), mouse_keys(0), allowDrag(false), allowDrop(false), skin(0), state("Normal"), skip_mouse(false), + WindowStyle(GWS_OVERLAPPED), NeedKeyFocus(false),BackgroundTiling(1,1),AutoTiling(false), + cursor("Arrow"), NonClientControl(false), Border(1), nc_mouse_keys(0) + { + ID = GUIManager::me()->GetNewID(); + hwnd = wndCounter++; + } + void GUIWindow::SetStyle(const String& name) + { + skin = GUIManager::me()->skinDefault[name]; + this->Invalidate(); + } + + GUIWindow* GUIWindow::PickWindow(const LifeMath::Vec2i& pos) + { + if(!childs.size()) + { + if(GetRectangleAbs().InRect(pos)) + return this; + else + return 0; + } + else + { + return 0; + } + } + GUIWindow* GUIWindow::PickChild(int x, int y) + { + if(childs.size()) + { + for(WindowList::reverse_iterator it = childs.rbegin(); it!= childs.rend(); ++it) + { + GUIWindow* wnd = *it; + if(wnd->skip_mouse) + wnd = wnd->PickChild(x, y); + + if(wnd !=0 && wnd->GetRectangleAbs().InRect(x, y) && wnd->IsEnabled() && wnd->mouseEnabled && wnd->IsVisible()) + return (*it); + } + } + return 0; + } + + GUIOverlay* GUIWindow::FindOverlay() + { + if(parent) + return parent->FindOverlay(); + else + return 0; + } + + void GUIWindow::SetPosition(const LifeMath::Vec2i& _position) + { + if(position != _position) + Invalidate(); + + position = _position; + if(parent) + { + LifeMath::Rect2i r = parent->GetClientRectangleAbs(); + LifeMath::Vec2i new_abs = position + LifeMath::Vec2i(r.x, r.y); + + if(positionAbs != new_abs) + Invalidate(); + positionAbs = new_abs; + } + else + positionAbs = position; + + /*LifeMath::float4 pos; + pos.Z = static_cast(this->positionAbs.X); + pos.W = static_cast(this->positionAbs.Y); + pos.X = static_cast(GUIManager::me()->GetWidth()); + pos.Y = static_cast(GUIManager::me()->GetHeight());*/ + + //cb->Update(&pos, sizeof(LifeMath::float4)); + + //Invalidate(); + UpdateChildTransform(); + } +} \ No newline at end of file diff --git a/LifeGUI/src/GUIWindow.h b/LifeGUI/src/GUIWindow.h new file mode 100644 index 0000000..96f3164 --- /dev/null +++ b/LifeGUI/src/GUIWindow.h @@ -0,0 +1,607 @@ +#ifndef GUIWindow_h__ +#define GUIWindow_h__ + +#include +#include "math/LifeMath.h" +#include "LString.h" +#include "GUIVertex.h" +#include "IImage.h" +#include "GUIWidgetSkin.h" +#include "GUIMessaging.h" +#include +#include "math/Rectangle.h" +#include "GUIBorder.h" + +namespace LifeGUI +{ + enum DOCK_TYPE + { + DOCK_NONE = 0, + DOCK_LEFT, + DOCK_RIGHT, + DOCK_TOP, + DOCK_BOTTOM, + DOCK_FILL + }; + + enum ALIGN_TYPE + { + ALIGN_LEFT = 0, + ALIGN_RIGHT, + ALIGN_TOP, + ALIGN_BOTTOM + }; + + enum GUI_WINDOW_STYLE + { + GWS_OVERLAPPED = 0, + GWS_POPUP = 1 + }; + + + + struct DragInfo + { + String Name; + void* data; + }; + + class GUIManager; + class GUIWindow + { + public: + bool AbortFocusChange; + LifeMath::float2 BackgroundTiling; + bool AutoTiling; + bool NeedKeyFocus; + bool NonClientControl; + String cursor; + GUIBorder Border; + GUIWindow* GetParent() + { + return parent; + } + bool IsParent(GUIWindow* wnd) + { + if(!parent) + return false; + if(parent == wnd) + return true; + return parent->IsParent(wnd); + } + int WindowStyle; + virtual void SetBackColor(const LifeMath::float4& color) + { + Invalidate(); + BackColor = color; + } + virtual void SetStyle(const String& name); + + bool skip_mouse; + String state; + + void* UserData; + EVENT2(MouseEvent, GUIWindow*, int); + EVENT2(KeyboardEvent, GUIWindow*, int); + EVENT1(WindowEvent, GUIWindow*); + String skin_name; + WindowEvent Event_Resize; + WindowEvent Event_Move; + //WindowEvent Event_Drop; + + MouseEvent Event_NCMouseEnter; + MouseEvent Event_NCMouseLeave; + MouseEvent Event_NCMouseDown; + MouseEvent Event_NCMouseMove; + MouseEvent Event_NCMouseUp; + MouseEvent Event_NCMouseClick; + + MouseEvent Event_MouseEnter; + MouseEvent Event_MouseLeave; + MouseEvent Event_MouseDown; + MouseEvent Event_MouseMove; + MouseEvent Event_MouseUp; + MouseEvent Event_MouseClick; + + MouseEvent Event_GotFocus; + MouseEvent Event_LostFocus; + + KeyboardEvent Event_KeyDown; + KeyboardEvent Event_KeyUp; + + int mouse_keys; + int nc_mouse_keys; + + GUIWindow* PickWindow(const LifeMath::Vec2i& pos); + GUIWindow* PickChild(int x, int y); + GUIWindow* PickChild(const LifeMath::Vec2i& pos) + { + PickChild(pos.X, pos.Y); + } + virtual void Invalidate() + { + UpdateChildDock(); + } + bool mouseEnabled; + int ID; + LifeMath::float4 BackColor; + LifeMath::float4 ForeColor; + String name; + int hwnd; + + GUIWindow(); + virtual ~GUIWindow() + { + } + virtual void SetForeColor(LifeMath::float4 color) + { + ForeColor = color; + Invalidate(); + } + virtual void OnMousePress(int x, int y, int key) + { + SetFlag(mouse_keys, 1 << key); + Event_MouseDown.Invoke(this, key); + } + virtual void OnMouseRelease(int x, int y, int key) + { + Event_MouseUp.Invoke(this, key); + if(GetFlag(mouse_keys, 1 << key)){ + RemoveFlag(mouse_keys, 1 << key); + Event_MouseClick.Invoke(this, key); + } + } + virtual void OnMouseMove(int abs_x, int abs_y, int rel_x, int rel_y) + { + Event_MouseMove.Invoke(this, 0); + } + virtual void OnMouseEnter() + { + Event_MouseEnter.Invoke(this, 0); + } + virtual void OnMouseLeave() + { + Event_MouseLeave.Invoke(this, 0); + } + virtual void OnNCMousePress(int x, int y, int key) + { + SetFlag(nc_mouse_keys, 1 << key); + Event_NCMouseDown.Invoke(this, key); + } + virtual void OnNCMouseRelease(int x, int y, int key) + { + Event_NCMouseUp.Invoke(this, key); + if(GetFlag(nc_mouse_keys, 1 << key)) + { + RemoveFlag(nc_mouse_keys, 1 << key); + Event_NCMouseClick.Invoke(this, key); + } + } + virtual void OnNCMouseMove(int abs_x, int abs_y, int rel_x, int rel_y) + { + Event_NCMouseMove.Invoke(this, 0); + } + virtual void OnNCMouseEnter() + { + Event_NCMouseEnter.Invoke(this, 0); + } + virtual void OnNCMouseLeave() + { + Event_NCMouseLeave.Invoke(this, 0); + } + + virtual void OnKeyDown(LifeCore::KeyCodes::Key key, unsigned int text, const std::string& str) + { + Event_KeyDown.Invoke(this, key); + } + virtual void OnKeyUp(LifeCore::KeyCodes::Key key, unsigned int text, const std::string& str) + { + Event_KeyUp.Invoke(this, key); + } + GUIWindow* GetWindowAtPoint(int x, int y) + { + if(GetRectangleAbs().InRect(x, y) && mouseEnabled && IsVisible()) + { + GUIWindow* child = PickChild(x, y); + if(child) + { + return child->GetWindowAtPoint(x, y); + } + else + { + if(!this->skip_mouse) + return this; + } + } + return 0; + } + virtual GUIOverlay* FindOverlay(); + LifeMath::Vec2i GetPosition() + { + return position; + } + LifeMath::Vec2i GetPositionAbs() + { + return positionAbs; + } + int GetWidth() const + { + return width; + } + LifeMath::Vec2i CursorToLocal(const LifeMath::Vec2i& world_coord) + { + return world_coord - positionAbs; + } + LifeMath::Vec2i CursorToClient(const LifeMath::Vec2i& world_coord) + { + LifeMath::Rect2i r = GetClientRectangleAbs(); + return world_coord - LifeMath::Vec2i(r.x, r.y); + } + LifeMath::Vec2i CursorToWorld(const LifeMath::Vec2i& local_coord) + { + return local_coord + positionAbs; + } + int GetHeight() const + { + return height; + } + void SetDock(DOCK_TYPE t) + { + if(dock != t) + { + dock = t; + if(parent != 0) + { + parent->Invalidate(); + } + } + } + DOCK_TYPE GetDock() + { + return dock; + } + bool IsVisible() + { + return visible; + } + void Show() + { + visible = true; + } + void Hide() + { + visible = false; + } + bool IsEnabled() + { + return enabled; + } + bool IsBorderEnabled() + { + return borderEnabled; + } + virtual void SetBorderEnabled(bool state) + { + borderEnabled = state; + Invalidate(); + } + virtual void RenderSelf() + { + } + virtual void ReleaseFocus() + { + } + virtual void SetPosition(const LifeMath::Vec2i& _position); + virtual void AddChild(GUIWindow* val) + { + childs.push_back(val); + val->SetParent(this); + Invalidate(); + } + virtual int RemoveChild(int hwnd) + { + for(WindowList::iterator it = childs.begin(); it != childs.end(); ++it) + { + if((*it)->hwnd == hwnd) + { + childs.erase(it); + return 1; + } + } + return 0; + } + LifeMath::Rect2i GetContentRectangle() + { + LifeMath::Rect2i r; + if(!childs.size()) + return r; + r = childs[0]->GetRectangle(); + + for(WindowList::iterator it = childs.begin(); it != childs.end(); ++it) + { + r.Merge((*it)->GetRectangle()); + } + return r; + } + virtual void SetParent(GUIWindow* val) + { + parent = val; + SetPosition(position); + UpdateScissor(); + Invalidate(); + } + virtual void SetWidth(int val) + { + if(width != val) + { + width = val; + Invalidate(); + Event_Resize.Invoke(this); + } + } + virtual void SetHeight(int val) + { + if(height != val) + { + height = val; + Invalidate(); + Event_Resize.Invoke(this); + } + } + // TODO trololo every day + virtual void Render() + { + RenderSelf(); + RenderChilds(); + } + virtual LifeMath::Rect2i GetClientRectangle() + { + return LifeMath::Rect2i(0, 0, this->GetWidth(), this->GetHeight()); + } + virtual LifeMath::Rect2i GetClientRectangleAbs() + { + LifeMath::Vec2i pos = this->GetPositionAbs(); + return LifeMath::Rect2i(pos.X, pos.Y, this->GetWidth(), this->GetHeight()); + } + + virtual LifeMath::Rect2i GetRectangle() + { + LifeMath::Vec2i rel = GetPosition(); + return LifeMath::Rect2i(rel.X, rel.Y,this->GetWidth(), this->GetHeight()); + } + virtual LifeMath::Rect2i GetRectangleAbs() + { + LifeMath::Vec2i abs = GetPositionAbs(); + return LifeMath::Rect2i(abs.X,abs.Y,this->GetWidth(), this->GetHeight()); + } + + bool IsAllowDrag() + { + return allowDrag; + } + bool IsAllowDrop() + { + return allowDrop; + } + virtual void FinishDrag() + { + } + virtual bool DragEnter(DragInfo* info) + { + return false; + } + virtual void AcceptDrop(DragInfo* info) + { + } + void SetScissor(const LifeMath::Rect2i& _scissor) + { + scissor = _scissor; + scissor_local = _scissor; + scissor_client = _scissor; + } + protected: + bool allowDrop; + bool allowDrag; + + int mouseButtons; + bool enabled; + bool visible; + bool mouseCaptured; + GUIWidgetSkin* skin; + bool borderEnabled; + //int borderSize; + int width; + int height; + GUIWindow* parent; + typedef std::vector WindowList; + typedef std::vector::iterator WindowListIter; + WindowList childs; + LifeMath::Vec2i position; + LifeMath::Vec2i positionAbs; + GUIVertex* vert; + DOCK_TYPE dock; + LifeMath::Rect2i scissor; + LifeMath::Rect2i scissor_local; + LifeMath::Rect2i scissor_client; + + const LifeMath::Rect2i& GetScissor() + { + return scissor; + } + + const LifeMath::Rect2i& GetScissorClient() + { + return scissor_client; + } + void UpdateScissor() + { + LifeMath::Rect2i sc; + sc = GetRectangleAbs(); + if(parent) + { + if(NonClientControl) + sc.Crop(parent->GetScissor()); + else + sc.Crop(parent->GetScissorClient()); + } + + LifeMath::Rect2i sc_t = sc; + LifeMath::Vec2i pos = CursorToLocal(LifeMath::Vec2i(sc_t.x, sc_t.y)); + sc_t.x = pos.X; + sc_t.y = pos.Y; + + if(scissor != sc || sc_t != scissor_local) + //if(sc_t != scissor_local) + { + scissor = sc; + scissor_local = sc_t; + sc = GetClientRectangleAbs(); + if(parent) + sc.Crop(parent->GetScissorClient()); + scissor_client = sc; + Invalidate(); + } + /* else if (sc_t != scissor_local) + { + scissor_local = sc_t; + Invalidate(); + }*/ + for(WindowList::iterator it = childs.begin(); it!= childs.end(); ++it) + { + if((*it)->IsVisible()) + (*it)->UpdateScissor(); + } + } + void UpdateChildDock() + { + LifeMath::Rect2i available_area = this->GetClientRectangle(); + + for(WindowList::iterator it = childs.begin(); it!= childs.end(); ++it) + { + GUIWindow* child = (*it); + if(child->IsVisible()) + { + DOCK_TYPE d = child->GetDock(); + LifeMath::Rect2i r = this->GetClientRectangle(); + + switch(d) + { + case DOCK_LEFT: + { + if(available_area.GetSquare() > 0) + { + child->SetPosition(LifeMath::Vec2i(available_area.x, available_area.y)); + child->SetHeight(available_area.height); + available_area.width -= child->GetWidth(); + available_area.x += child->GetWidth(); + } + else + { + child->SetPosition(LifeMath::Vec2i(0, 0)); + child->SetHeight(r.height); + } + } + break; + case DOCK_RIGHT: + { + if(available_area.GetSquare() > 0) + { + child->SetPosition(LifeMath::Vec2i(available_area.width - child->GetWidth() + available_area.x, 0)); + child->SetHeight(available_area.height); + available_area.width -= child->GetWidth(); + } + else + { + child->SetPosition(LifeMath::Vec2i(r.width - child->GetWidth(), 0)); + child->SetHeight(r.height); + } + } + break; + case DOCK_TOP: + { + if(available_area.GetSquare() > 0) + { + child->SetPosition(LifeMath::Vec2i(available_area.x, available_area.y)); + child->SetWidth(available_area.width); + available_area.height -= child->GetHeight(); + available_area.y += child->GetHeight(); + } + else + { + child->SetPosition(LifeMath::Vec2i(0, 0)); + child->SetWidth(r.width); + } + } + break; + case DOCK_BOTTOM: + { + if(available_area.GetSquare() > 0) + { + child->SetPosition(LifeMath::Vec2i(available_area.x, available_area.height - child->GetHeight() + available_area.y)); + child->SetWidth(available_area.width); + available_area.height -= child->GetHeight(); + } + else + { + child->SetPosition(LifeMath::Vec2i(0, r.height - child->GetHeight())); + child->SetWidth(r.width); + } + } + break; + case DOCK_FILL: + { + if(available_area.GetSquare() > 0) + { + child->SetPosition(LifeMath::Vec2i(available_area.x, available_area.y)); + child->SetWidth(available_area.width); + child->SetHeight(available_area.height); + available_area.width = 0; + available_area.height = 0; + } + else + { + child->SetPosition(LifeMath::Vec2i(0, 0)); + child->SetWidth(r.width); + child->SetHeight(r.height); + } + } + break; + } + + //fix up available area rectangle + if(available_area.width < 0) + available_area.width = 0; + if(available_area.height < 0) + available_area.height = 0; + } + } + } + + void RenderChilds() + { + for(WindowList::iterator it = childs.begin(); it!= childs.end(); ++it) + { + if((*it)->IsVisible()) + (*it)->Render(); + } + } + void UpdateChildTransform() + { + for(WindowList::iterator it = childs.begin(); it!= childs.end(); ++it) + { + GUIWindow* w = (*it); + if(w->IsVisible()) + w->SetPosition(w->GetPosition()); + } + } + /*void UpdateChilds(float dt) + { + for(WindowList::iterator it = childs.begin(); it!= childs.end(); ++it) + { + if((*it)->IsVisible()) + (*it)->Update( dt); + } + }*/ + }; +} +#endif // GUIWindow_h__ \ No newline at end of file diff --git a/LifeGUI/src/IGUICursor.h b/LifeGUI/src/IGUICursor.h new file mode 100644 index 0000000..ce99487 --- /dev/null +++ b/LifeGUI/src/IGUICursor.h @@ -0,0 +1,19 @@ +#ifndef IGUICursor_h__ +#define IGUICursor_h__ + +#include "IImage.h" +#include "GUIWidgetSkin.h" + +namespace LifeGUI +{ + class IGUICursor + { + public: + IImage* tex; + GUIWidgetSkin* skin; + String name; + LifeMath::Vec2i offset; + LifeMath::Vec2i size; + }; +} +#endif // IGUICursor_h__ \ No newline at end of file diff --git a/LifeGUI/src/IGUIDataManager.h b/LifeGUI/src/IGUIDataManager.h new file mode 100644 index 0000000..a1c524a --- /dev/null +++ b/LifeGUI/src/IGUIDataManager.h @@ -0,0 +1,20 @@ +#ifndef IGUIDataManager_h__ +#define IGUIDataManager_h__ + +#include +#include +#include "IImage.h" + +namespace LifeGUI +{ + class IGUIDataManager + { + public: + virtual LifeCore::IFileSystem* getFileSystem() = 0; + virtual LifeGUI::IImage* getImage(const String& name) = 0; + virtual LifeGUI::IImage* createImage(uint32 width, uint32 height) = 0; + virtual const String getGuiRootDir() = 0; + }; +} + +#endif // IGUIDataManager_h__ diff --git a/LifeGUI/src/IGUIRenderer.cpp b/LifeGUI/src/IGUIRenderer.cpp new file mode 100644 index 0000000..839613d --- /dev/null +++ b/LifeGUI/src/IGUIRenderer.cpp @@ -0,0 +1,46 @@ +#include "IGUIRenderer.h" +#include "GUIManager.h" +#include + +namespace LifeGUI +{ + IGUIRenderer::IGUIRenderer() + :batch_id(0) + { + buffer = new LifeCore::IMemoryStream(sizeof(GUIVertex) * MAX_GUI_VERTEX_COUNT); + buffer->Seek(0); + } + void IGUIRenderer::Reset() + { + buffer->Seek(0); + batch_id = 0; + memset(&batch[0], 0, MAX_DIP_COUNT * sizeof(GUIBatch)); + } + void IGUIRenderer::SetTexture(IImage* tex, IImage* font, bool line_geom) + { + //start new batch if _any_ condition is changed + if((tex != 0 && batch[batch_id].tex != 0 && (batch[batch_id].tex->GetID() != tex->GetID())) || + (tex != 0 && batch[batch_id].tex == 0) || (tex == 0 && batch[batch_id].tex != 0)) + batch_id++; + else if( font != 0 && batch[batch_id].font != 0 && (batch[batch_id].font->GetID() != font->GetID())) + batch_id++; + else if (batch[batch_id].lines != line_geom) + batch_id++; + + assert(batch_id < MAX_DIP_COUNT && "GUI batch count overflow!"); + + if(font != 0) batch[batch_id].font = font; + if(tex != 0) batch[batch_id].tex = tex; + batch[batch_id].lines = line_geom; + } + void IGUIRenderer::Write(const GUIVertex& data) + { + buffer->Write(&data, sizeof(GUIVertex)); + batch[batch_id].vertex_count++; + } + void IGUIRenderer::Write(const GUIVertex* data, size_t count) + { + buffer->Write(data, sizeof(GUIVertex) * count); + batch[batch_id].vertex_count += count; + } +} \ No newline at end of file diff --git a/LifeGUI/src/IGUIRenderer.h b/LifeGUI/src/IGUIRenderer.h new file mode 100644 index 0000000..07b2490 --- /dev/null +++ b/LifeGUI/src/IGUIRenderer.h @@ -0,0 +1,45 @@ +#ifndef IGUIRenderer_h__ +#define IGUIRenderer_h__ + +#include "IImage.h" +#include "GUIVertex.h" +#include +#include +#define MAX_DIP_COUNT 128 +#define MAX_GUI_VERTEX_COUNT (6 * 4096) + +namespace LifeGUI +{ + struct GUIBatch + { + IImage* tex; + IImage* font; + bool lines; + int tex_id; + size_t vertex_count; + GUIBatch() + :tex(nullptr), font(nullptr), vertex_count(0) + { + lines = false; + tex_id = 0; + } + }; + class IGUIRenderer + { + public: + IGUIRenderer(); + void Reset(); + void SetTexture(IImage* tex, IImage* font, bool line_geom); + void Write(const GUIVertex& data); + void Write(const GUIVertex* data, size_t count); + virtual void SetScissor(const LifeMath::Rect2i& rect) = 0; + virtual void Render() = 0; + virtual void SetScreenSize(int w, int h) = 0; + protected: + LifeCore::IMemoryStream* buffer; + LifeGUI::GUIBatch batch[MAX_DIP_COUNT]; + size_t batch_id; + }; +} + +#endif // IGUIRenderer_h__ \ No newline at end of file diff --git a/LifeGUI/src/IImage.h b/LifeGUI/src/IImage.h new file mode 100644 index 0000000..9c4a3bb --- /dev/null +++ b/LifeGUI/src/IImage.h @@ -0,0 +1,28 @@ +#ifndef IImage_h__ +#define IImage_h__ +#include +#include +#include + +namespace LifeGUI +{ + class IImage + { + public: + virtual size_t GetID() = 0; + virtual uint32 GetWidth() = 0; + virtual uint32 GetHeight() = 0; + virtual uint32 GetPixelSize() = 0; + virtual void SetPixel(uint32 x, uint32 y, const void* data) = 0; + virtual void GetPixel(uint32 x, uint32 y, void* data) = 0; + virtual void SetData(const void* data, uint32 size) = 0; + virtual void SetData(const void* data, const LifeMath::Rect2i& area) = 0; + virtual void GetData(void* data, uint32 size) = 0; + virtual void GetData(void* data, const LifeMath::Rect2i& area) = 0; + + virtual LifeCore::IDataRectangle* Map(uint32 level, bool read, bool write) = 0; + virtual void Unmap(uint32 level) = 0; + + }; +} +#endif // IImage_h__ \ No newline at end of file diff --git a/LifeGUI/src/PropertyItemFile.h b/LifeGUI/src/PropertyItemFile.h new file mode 100644 index 0000000..879120b --- /dev/null +++ b/LifeGUI/src/PropertyItemFile.h @@ -0,0 +1,43 @@ +#ifndef PropertyItemFile_h__ +#define PropertyItemFile_h__ +#include "IPropertyItem.h" +#include "GUIFileBrowserDialog.h" +#include "GUIManager.h" + +namespace LifeGUI +{ + class PropertyItemFile : public LifeCore::IPropertyItem + { + public: + PropertyItemFile(IFileSystem* fs, LifeCore::Property* _prop) + { + dialog = new GUIFileBrowserDialog(); + dialog->SetFileSystem(fs); + dialog->Hide(); + prop = _prop; + } + PropertyEditMode GetEditMode() + { + return EM_CUSTOM; + } + void OnBeginEdit() + { + GUIManager::me()->GetActiveOverlay()->AddChild(dialog); + dialog->Show(); + } + void OnEndEdit(const String& data) + { + dialog->Hide(); + GUIManager::me()->GetActiveOverlay()->RemoveChild(dialog->hwnd); + } + String ToString() + { + return (*prop)(); + } + private: + GUIFileBrowserDialog* dialog; + LifeCore::Property* prop; + }; +} + +#endif // PropertyItemFile_h__ \ No newline at end of file diff --git a/LifeGUI/src/PropertyItemFloat.h b/LifeGUI/src/PropertyItemFloat.h new file mode 100644 index 0000000..7bf1c91 --- /dev/null +++ b/LifeGUI/src/PropertyItemFloat.h @@ -0,0 +1,32 @@ +#ifndef PropertyItemFloat_h__ +#define PropertyItemFloat_h__ +#include "IPropertyItem.h" + +namespace LifeGUI +{ + class PropertyItemFloat : public LifeCore::IPropertyItem + { + public: + PropertyItemFloat( LifeCore::Property* _prop) + { + prop = _prop; + } + PropertyEditMode GetEditMode() + { + return EM_INPLACE; + } + void OnEndEdit(const String& data) + { + *prop = (float)atof(data.c_str()); + } + String ToString() + { + char buffer[64]; + sprintf(buffer, "%f", (*prop)()); + return String(buffer); + } + private: + LifeCore::Property* prop; + }; +} +#endif // PropertyItemFloat_h__ \ No newline at end of file diff --git a/LifeGame/LifeGame.vcxproj b/LifeGame/LifeGame.vcxproj new file mode 100644 index 0000000..f63a534 --- /dev/null +++ b/LifeGame/LifeGame.vcxproj @@ -0,0 +1,211 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {8BE9EDA5-A27E-48BF-BBA7-36139D47F5D9} + LifeGame + Win32Proj + <Project Location In Database> + <Source Control Database> + <Local Binding Root of Project> + Mercurial Source Control Package + $(VCTargetsPath11) + + + + StaticLibrary + MultiByte + true + v120 + + + StaticLibrary + MultiByte + true + v120 + + + StaticLibrary + MultiByte + v120 + + + StaticLibrary + MultiByte + v120 + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(SolutionDir)$(ProjectName)\lib\ + $(SolutionDir)\lib\$(Configuration)\ + $(SolutionDir)$(ProjectName)\tmp\$(Configuration)\ + $(Configuration)\$(ProjectName) + $(SolutionDir)$(ProjectName)\lib\ + $(SolutionDir)\lib\$(Configuration)\ + $(SolutionDir)$(ProjectName)\tmp\$(Configuration)\ + $(Configuration)\ + + + $(ProjectName)_d + + + + Disabled + $(REPO_DIR)/angelscript/include;$(REPO_DIR)\bullet-2.78\src;$(SolutionDir)\LifeGraphics\src;.\src;$(SolutionDir)\LifeCore\src;$(SolutionDir)\LifeGUI\src;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + false + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + EditAndContinue + true + + + LifeGUI_d.lib;angelscriptd.lib;LifeGraphics_d.lib;LifeCore_d.lib;%(AdditionalDependencies) + lib\LifeGame_d.lib + $(SolutionDir)\LifeGUI\lib;$(SolutionDir)\LifeGraphics\lib;$(REPO_DIR)/angelscript/lib;$(REPO_DIR)\bullet-2.78\bullet-2.78\lib;$(DXSDK_DIR)\Lib\x86;$(SolutionDir)\LifeCore\lib;%(AdditionalLibraryDirectories) + + + + + Disabled + $(REPO_DIR)/angelscript/include;$(REPO_DIR)\bullet-2.78\src;$(SolutionDir)\LifeEngine\include;.\include;$(SolutionDir)\LifeCore\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + false + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + ProgramDatabase + true + + + angelscriptd.lib;LifeEngine_d.lib;LifeCore_d.lib;%(AdditionalDependencies) + lib\LifeGame_d.lib + $(SolutionDir)\LifeEngine\lib;$(REPO_DIR)/angelscript/lib;$(REPO_DIR)\bullet-2.78\bullet-2.78\lib;$(DXSDK_DIR)\Lib\x86;$(SolutionDir)\LifeCore\lib;%(AdditionalLibraryDirectories) + + + + + MaxSpeed + true + $(REPO_DIR)/angelscript/include;$(REPO_DIR)\bullet-2.78\src;$(SolutionDir)\LifeGraphics\src;.\src;$(SolutionDir)\LifeCore\src;$(SolutionDir)\LifeGUI\src;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + true + + + OIS_Static.lib;%(AdditionalDependencies) + lib\LifeGame.lib + $(SolutionDir)\LifeEngine\lib;$(REPO_DIR)\bullet-2.78\bullet-2.78\lib;$(REPO_DIR)\ois-v1-3\lib;$(DXSDK_DIR)\Lib\x86;%(AdditionalLibraryDirectories) + + + + + MaxSpeed + true + $(REPO_DIR)/angelscript/include;$(REPO_DIR)\bullet-2.78\src;$(REPO_DIR)\ois-v1-3\includes;$(SolutionDir)\LifeEngine\include;.\include;$(SolutionDir)\LifeCore\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + true + + + OIS_Static.lib;%(AdditionalDependencies) + lib\LifeGame.lib + $(SolutionDir)\LifeEngine\lib;$(REPO_DIR)\bullet-2.78\bullet-2.78\lib;$(REPO_DIR)\ois-v1-3\lib;$(DXSDK_DIR)\Lib\x86;%(AdditionalLibraryDirectories) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LifeGame/src/Action.cpp b/LifeGame/src/Action.cpp new file mode 100644 index 0000000..89e64ea --- /dev/null +++ b/LifeGame/src/Action.cpp @@ -0,0 +1,28 @@ +#include "Action.h" +#include +#include "IActionListener.h" + +namespace LifeGame +{ + Action::Action(const ActionId& id) + :actionId(id){ + + } + void Action::AddListener(LifeGame::IActionListener* listener){ + lock.lock(); + listeners.push_back(listener); + lock.unlock(); + } + void Action::RemoveListener(LifeGame::IActionListener* listener){ + assert(false && "Not implemented yet"); + } + void Action::Execute(const ActionParam& param){ + lock.lock(); + for(auto listener : listeners) + listener->OnAction(actionId, param); + lock.unlock(); + } + const ActionId& Action::GetID(){ + return actionId; + } +} \ No newline at end of file diff --git a/LifeGame/src/Action.h b/LifeGame/src/Action.h new file mode 100644 index 0000000..680dbbb --- /dev/null +++ b/LifeGame/src/Action.h @@ -0,0 +1,37 @@ +#ifndef Action_h__ +#define Action_h__ + +#include +#include +#include + +namespace LifeGame +{ + struct ActionParam{ + union action_val{ + int si32_val; + float f_val; + }; + action_val val; + }; + + typedef String ActionId; + class IActionListener; + + class Action final + { + public: + Action(const ActionId& id); + void AddListener(LifeGame::IActionListener* listener); + void RemoveListener(LifeGame::IActionListener* listener); + void Execute(const ActionParam& param); + const ActionId& GetID(); + private: + typedef std::vector ActionListenerCollection; + typedef std::vector::iterator ActionListenerCollectionIt; + ActionListenerCollection listeners; + ActionId actionId; + std::mutex lock; + }; +} +#endif // Action_h__ \ No newline at end of file diff --git a/LifeGame/src/ActionSystem.cpp b/LifeGame/src/ActionSystem.cpp new file mode 100644 index 0000000..cf8b08f --- /dev/null +++ b/LifeGame/src/ActionSystem.cpp @@ -0,0 +1,33 @@ +#include "ActionSystem.h" +#include +namespace LifeGame +{ + Action* ActionSystem::AddAction(const ActionId& id){ + std::lock_guard lg(action_mutex); + auto action = GetActionNoLock(id); + if(action == nullptr){ + Action* act = new Action(id); + actions[id] = act; + return act; + } + return action; + } + Action* ActionSystem::GetAction(const ActionId& id){ + std::lock_guard lg(action_mutex); + return GetActionNoLock(id); + + } + Action* ActionSystem::GetActionNoLock(const ActionId& id){ + auto it = actions.find(id); + if(it != actions.end()) + return it->second; + return nullptr; + } + void ActionSystem::DoAction(const ActionId& id, const ActionParam& param){ + std::lock_guard lg(action_mutex); + Action* act = GetActionNoLock(id); + if(act != nullptr){ + act->Execute(param); + } + } +} \ No newline at end of file diff --git a/LifeGame/src/ActionSystem.h b/LifeGame/src/ActionSystem.h new file mode 100644 index 0000000..040f4d5 --- /dev/null +++ b/LifeGame/src/ActionSystem.h @@ -0,0 +1,28 @@ +#ifndef EventSystem_h__ +#define EventSystem_h__ + +#include +#include +#include "Action.h" +#include + +namespace LifeGame +{ + class ActionSystem : public Singleton + { + friend class Singleton; + public: + ~ActionSystem(){}; + Action* AddAction(const ActionId& id); + Action* GetAction(const ActionId& id); + void DoAction(const ActionId& id, const ActionParam& param); + private: + Action* GetActionNoLock(const ActionId& id); + ActionSystem(){}; + typedef std::map ActionCollection; + typedef std::map::iterator ActionCollectionIt; + ActionCollection actions; + std::mutex action_mutex; + }; +} +#endif // EventSystem_h__ diff --git a/LifeGame/src/BulletLibs.h b/LifeGame/src/BulletLibs.h new file mode 100644 index 0000000..d47f30f --- /dev/null +++ b/LifeGame/src/BulletLibs.h @@ -0,0 +1,12 @@ +#ifndef BulletLibs_h__ +#define BulletLibs_h__ + +#pragma comment (lib, "BulletCollision.lib") +#pragma comment (lib, "BulletDynamics.lib") +#pragma comment (lib, "BulletSoftBody.lib") +#pragma comment (lib, "LinearMath.lib") +#pragma comment (lib, "ConvexDecomposition.lib") +#pragma comment (lib, "BulletMultiThreaded.lib") +#pragma comment (lib, "GIMPACTUtils.lib") + +#endif // BulletLibs_h__ \ No newline at end of file diff --git a/LifeGame/src/CGUIDataManager.cpp b/LifeGame/src/CGUIDataManager.cpp new file mode 100644 index 0000000..ba63c98 --- /dev/null +++ b/LifeGame/src/CGUIDataManager.cpp @@ -0,0 +1,21 @@ +#include "CGUIDataManager.h" +#include +#include "CImage.h" + +namespace LifeGame +{ + LifeCore::IFileSystem* CGUIDataManager::getFileSystem(){ + return LifeGraphics::ResourceManager::me()->getFileSystem(); + } + LifeGUI::IImage* CGUIDataManager::getImage(const String& name){ + LifeGraphics::ITexture2D* tex = LifeGraphics::ResourceManager::me()->GetTexture(AS_STRING("root/Data/GUI/") + name).ptr(); + return CImage::FromTexture(tex); + } + const String CGUIDataManager::getGuiRootDir(){ + return AS_STRING("root/Data/GUI/"); + } + LifeGUI::IImage* CGUIDataManager::createImage(uint32 width, uint32 height){ + LifeGraphics::ITexture2D* tex = LifeGraphics::CG::me()->GetRenderer()->CreateTexture2D(0, 1, width, height, FORMAT_R8G8B8A8_UNORM); + return CImage::FromTexture(tex); + } +} \ No newline at end of file diff --git a/LifeGame/src/CGUIDataManager.h b/LifeGame/src/CGUIDataManager.h new file mode 100644 index 0000000..2e817d9 --- /dev/null +++ b/LifeGame/src/CGUIDataManager.h @@ -0,0 +1,17 @@ +#ifndef CGUIDataManager_h__ +#define CGUIDataManager_h__ + +#include + +namespace LifeGame +{ + class CGUIDataManager : public LifeGUI::IGUIDataManager + { + public: + LifeCore::IFileSystem* getFileSystem(); + LifeGUI::IImage* getImage(const String& name); + const String getGuiRootDir() override; + LifeGUI::IImage* createImage(uint32 width, uint32 height); + }; +} +#endif // CGUIDataManager_h__ diff --git a/LifeGame/src/CGUIRenderer.cpp b/LifeGame/src/CGUIRenderer.cpp new file mode 100644 index 0000000..2492f03 --- /dev/null +++ b/LifeGame/src/CGUIRenderer.cpp @@ -0,0 +1,121 @@ + +#include "CGUIRenderer.h" +#include "ResourceManager.h" +#include "CImage.h" +#include + +namespace LifeGame +{ + CGUIRenderer::CGUIRenderer(LifeGraphics::IRenderer* renderer) + :scr_w(320), scr_h(240) + { + pRenderer = renderer; + + blendState = pRenderer->CreateBlendState(true, false, BLEND_OP_ADD, BLEND_SRC_ALPHA, BLEND_INV_SRC_ALPHA, 1 | 2 | 4 | 8); + depthState = pRenderer->CreateDSState(false, false, COMPARISON_NEVER); + + cb = pRenderer->CreateConstantBuffer(sizeof(LifeMath::float4)); + LifeMath::float4 c((float)scr_w, (float)scr_h,0,0); + cb->Update(&c, sizeof(LifeMath::float4)); + + vsGui = LifeGraphics::ResourceManager::me()->CompileUserShaderVS(AS_STRING("GUI.vsh")); + psGui = LifeGraphics::ResourceManager::me()->CompileUserShaderPS(AS_STRING("GUI.psh")); + + LifeGraphics::IInputElement elements[] = + { + LifeGraphics::IInputElement("POSITION", 0, 0, FORMAT_R32G32B32_FLOAT, 0, 0, 0), + LifeGraphics::IInputElement("WidgetColor", 0, 12, FORMAT_R32G32B32A32_FLOAT, 0, 0, 0), + LifeGraphics::IInputElement("TEXCOORD", 0, 28, FORMAT_R32G32B32A32_FLOAT, 0, 0, 0), + LifeGraphics::IInputElement("TileInfo", 0, 44, FORMAT_R32G32B32A32_FLOAT, 0, 0, 0), + }; + + ilGui = renderer->CreateInputLayout(elements, 4, vsGui); + + //vb = renderer->CreateVertexBuffer(MAX_GUI_VERTEX_COUNT * sizeof(LifeGUI::GUIVertex), buffer->GetData(), sizeof(LifeGUI::GUIVertex)); + vb = renderer->CreateVertexBufferDynamic(MAX_GUI_VERTEX_COUNT * sizeof(LifeGUI::GUIVertex), sizeof(LifeGUI::GUIVertex)); + + LifeGraphics::ISamplerDescription psDesc(LifeGraphics::Wrap, LifeGraphics::Wrap, LifeGraphics::Wrap, LifeGraphics::MinMagMipPoint); + + pointSampler = renderer->CreateTextureSampler(&psDesc); + } + void CGUIRenderer::Render() + { + //printf("Batch ID: %i\n", batch_id); + + if(buffer->Tell()) + { + vb->Map(0, LifeGraphics::LF_Discard | LifeGraphics::LF_NoOverwrite)->Write(buffer->GetData(), buffer->Tell()); + vb->Unmap(); + + //vb->Update(buffer->GetData(), buffer->Tell()); + LifeMath::Rect2i vp(scr_w, scr_h); + pRenderer->SetViewport(vp); + pRenderer->SetScissorRectangle(vp); + + pRenderer->SetVertexShader(vsGui); + pRenderer->SetPixelShader(psGui); + pRenderer->SetVertexBuffer(vb); + pRenderer->SetVSConstantBuffer(cb, 0); + pRenderer->SetInputLayout(ilGui); + + pRenderer->SetPSTextureSampler(pointSampler, 0); + + pRenderer->SetDepthStencilState(depthState); + pRenderer->SetBlendState(blendState); + pRenderer->SetPrimitiveTopology(PT_TRIANGLE_LIST); + + int prev_topology = PT_TRIANGLE_LIST; + int tmp_topology; + + size_t offset = 0; + for(size_t i = 0; i <= batch_id; i++) + { + LifeGUI::GUIBatch* b = &batch[i]; + + if(b->vertex_count > 0) + { + tmp_topology = b->lines ? PT_LINE_LIST : PT_TRIANGLE_LIST; + + if(prev_topology != tmp_topology) + { + prev_topology = tmp_topology; + pRenderer->SetPrimitiveTopology((PRIMITIVE_TOPOLOGY)tmp_topology); + } + + CImage* tex; + if(b->tex) + { + tex = reinterpret_cast(b->tex); + pRenderer->SetPSTexture(tex->GetTexture(), 0); + } + else{ + pRenderer->SetPSTexture(reinterpret_cast(LifeGUI::GUIManager::me()->DIFF)->GetTexture(), 0); + } + + if(b->font) + { + tex = reinterpret_cast(b->font); + pRenderer->SetPSTexture(tex->GetTexture(), 1); + } + + pRenderer->Draw(b->vertex_count, offset); + + offset += b->vertex_count; + } + } + } + } + void CGUIRenderer::SetScissor(const LifeMath::Rect2i& rect) + { + pRenderer->SetScissorRectangle(rect); + pRenderer->SetViewport(rect); + } + void CGUIRenderer::SetScreenSize(int w, int h) + { + scr_w = w; + scr_h = h; + + LifeMath::float4 c((float)scr_w, (float)scr_h,0,0); + cb->Update(&c, sizeof(LifeMath::float4)); + } +} \ No newline at end of file diff --git a/LifeGame/src/CGUIRenderer.h b/LifeGame/src/CGUIRenderer.h new file mode 100644 index 0000000..1c8dce4 --- /dev/null +++ b/LifeGame/src/CGUIRenderer.h @@ -0,0 +1,31 @@ + +#ifndef CGUIRenderer_h__ +#define CGUIRenderer_h__ + +#include +#include +#include + +namespace LifeGame +{ + class CGUIRenderer : public LifeGUI::IGUIRenderer + { + public: + CGUIRenderer(LifeGraphics::IRenderer* renderer); + void Render(); + void SetScissor(const LifeMath::Rect2i& rect); + void SetScreenSize(int w, int h); + private: + LifeGraphics::IRenderer* pRenderer; + LifeGraphics::IShader* vsGui; + LifeGraphics::IShader* psGui; + LifeGraphics::IInputLayout* ilGui; + LifeGraphics::ITextureSampler* pointSampler; + LifeGraphics::IConstantBuffer* cb; + LifeGraphics::IVertexBuffer* vb; + LifeGraphics::IBlendState* blendState; + LifeGraphics::IDepthStencilState* depthState; + int scr_w, scr_h; + }; +} +#endif // CGUIRenderer_h__ \ No newline at end of file diff --git a/LifeGame/src/CImage.cpp b/LifeGame/src/CImage.cpp new file mode 100644 index 0000000..d6785b7 --- /dev/null +++ b/LifeGame/src/CImage.cpp @@ -0,0 +1,64 @@ +#include "CImage.h" + +namespace LifeGame +{ + CImage::CImage() + :pTex(0) + { + } + + LifeCore::IDataRectangle* CImage::Map(uint32 level, bool read, bool write){ + return pTex->Map(level, read, write); + } + void CImage::Unmap(uint32 level){ + pTex->Unmap(level); + } + CImage* CImage::FromTexture(LifeGraphics::ITexture2D* src) + { + CImage* pic = new CImage(); + pic->pTex = src; + return pic; + } + void CImage::Reset(LifeGraphics::ITexture2D* src) + { + pTex = src; + } + size_t CImage::GetID() + { + return pTex->GetID(); + } + uint32 CImage::GetWidth() + { + return pTex->Width(); + } + uint32 CImage::GetHeight() + { + return pTex->Height(); + } + uint32 CImage::GetPixelSize() + { + return pTex->bpp; + } + void CImage::SetPixel(uint32 x, uint32 y, const void* data) + { + } + void CImage::GetPixel(uint32 x, uint32 y, void* data) + { + } + void CImage::SetData(const void* data, uint32 size) + { + } + void CImage::SetData(const void* data, const LifeMath::Rect2i& area) + { + } + void CImage::GetData(void* data, uint32 size) + { + } + void CImage::GetData(void* data, const LifeMath::Rect2i& area) + { + } + LifeGraphics::ITexture2D* CImage::GetTexture() + { + return pTex; + } +} \ No newline at end of file diff --git a/LifeGame/src/CImage.h b/LifeGame/src/CImage.h new file mode 100644 index 0000000..bed1683 --- /dev/null +++ b/LifeGame/src/CImage.h @@ -0,0 +1,34 @@ + +#ifndef CImage_h__ +#define CImage_h__ +#include "render/IRenderer.h" +#include "IImage.h" + +namespace LifeGame +{ + class CImage : public LifeGUI::IImage + { + public: + static CImage* FromTexture(LifeGraphics::ITexture2D* src); + size_t GetID(); + uint32 GetWidth(); + uint32 GetHeight(); + uint32 GetPixelSize(); + void SetPixel(uint32 x, uint32 y, const void* data); + void GetPixel(uint32 x, uint32 y, void* data); + void SetData(const void* data, uint32 size); + void SetData(const void* data, const LifeMath::Rect2i& area); + void GetData(void* data, uint32 size); + void GetData(void* data, const LifeMath::Rect2i& area); + void Reset(LifeGraphics::ITexture2D* src); + LifeGraphics::ITexture2D* GetTexture(); + + LifeCore::IDataRectangle* Map(uint32 level, bool read, bool write); + void Unmap(uint32 level); + private: + CImage(); + LifeGraphics::ITexture2D* pTex; + }; +} + +#endif // CPicture_h__ \ No newline at end of file diff --git a/LifeGame/src/Entity.cpp b/LifeGame/src/Entity.cpp new file mode 100644 index 0000000..686f2f8 --- /dev/null +++ b/LifeGame/src/Entity.cpp @@ -0,0 +1,114 @@ +#include "Entity.h" + +namespace LifeGame +{ + Entity::Entity() + :bEnabled(true) + { + physics = NULL; + graphics = NULL; + bPhysicalized = false; + name = "unnamed_entity"; + } + Entity::~Entity() + { + } + void Entity::SetEnabled(bool state) + { + bEnabled = state; + } + bool Entity::IsEnabled() + { + return bEnabled; + } + const String& Entity::GetName() const + { + return name; + } + void Entity::SetName(const String& _name) + { + name = _name; + } + void Entity::Update(float timeElapsed){ + for(auto logic : logic_components){ + logic->Update(timeElapsed); + } + } + void Entity::Save() + { + pugi::xml_document doc; + pugi::xml_node root = doc.append_child("Entity"); + + root.append_attribute("Name") = this->name.c_str(); + root.append_attribute("bGraphics") = (graphics != NULL); + root.append_attribute("bPhysics") = (physics != NULL); + if(graphics) + { + pugi::xml_node visual = root.append_child("Graphics"); + //mesh block + visual.append_attribute("MeshFile") = (graphics->pMesh->Name).c_str(); + //mesh->materials block + pugi::xml_node materials = visual.append_child("Materials"); + for(int i = 0; ipMesh->getSumbeshCount(); i++) + { + materials.append_attribute("ID") = i; + materials.append_attribute("Name") = graphics->pMesh->getMaterial(i)->GetBaseMaterial()->Name.c_str(); + } + } + + if(physics) + { + pugi::xml_node phys = root.append_child("Physics"); + phys.append_attribute("fMass") = physics->GetMass(); + phys.append_attribute("bStatic") = physics->IsStatic(); + phys.append_attribute("fFriction") = 0.27f; + phys.append_attribute("sMeshName") = "Level2.ocf"; + } + + { + pugi::xml_node game = root.append_child("Game"); + // game.append_attribute("bPlayer") = IsPlayer(); + game.append_attribute("sScript") ="Actors/basic_player.lua"; + } + + LifeXMLWriter writer; + doc.print(writer); + LifeCore::IFile* file; + LifeGraphics::ResourceManager::me()->getFileSystem()->GetFile(AS_STRING("root/Data/Entities/") + name + AS_STRING(".xml"), &file); + LifeCore::IDataStreamPtr stm = file->OpenWrite(0); + stm->Write(writer.result.c_str(), writer.length); + stm.release(); + } + void Entity::SetPhysicsEntity(IPhysicsEntity* _phys) + { + physics = _phys; + } + void Entity::SetGraphicalEntity(IGraphicsEntity* _graph) + { + graphics = _graph; + graphics->pMesh->SetTransform(&transform); + } + + IPhysicsEntity* Entity::GetPhysicsEntity() + { + return physics; + } + IGraphicsEntity* Entity::GetGraphicalEntity() + { + return graphics; + } + + void Entity::CallScriptEvent(String eventName) + { + } + LifeCore::ITransform* Entity::GetTransform() + { + return &transform; + } + void Entity::AddLogicEntity(GOCLogic* logic){ + if(logic != nullptr){ + logic_components.push_back(logic); + } + } + +} \ No newline at end of file diff --git a/LifeGame/src/Entity.h b/LifeGame/src/Entity.h new file mode 100644 index 0000000..f9af13d --- /dev/null +++ b/LifeGame/src/Entity.h @@ -0,0 +1,93 @@ +#ifndef Entity_h__ +#define Entity_h__ +#include "IGraphicsEntity.h" +#include "IPhysicsEntity.h" +#include +#include "LString.h" +#include "XML.h" +#include "Action.h" +#include "GOCLogic.h" + +namespace LifeGame +{ + + enum ScriptEvent + { + ScriptEvent_Activate = 0x00000001, + ScriptEvent_Deactivate , + ScriptEvent_FireModeChange , + ScriptEvent_DropItem , + ScriptEvent_Reset , + ScriptEvent_Contact , + ScriptEvent_Enter , + ScriptEvent_Leave , + ScriptEvent_Timer , + ScriptEvent_StartAnimation , + ScriptEvent_AnimationKey , + ScriptEvent_EndAnimation , + ScriptEvent_Respawn , + ScriptEvent_ItemActivated , + ScriptEvent_Hit , + ScriptEvent_Fire , + ScriptEvent_WeaponReady , + ScriptEvent_StopFiring , + ScriptEvent_Reload , + ScriptEvent_Command , + ScriptEvent_FireGrenade , + ScriptEvent_Die , + ScriptEvent_ZoomToggle , + ScriptEvent_ZoomIn , + ScriptEvent_ZoomOut , + ScriptEvent_Land , + ScriptEvent_FireCancel , + ScriptEvent_GameDefinedEvent , + ScriptEvent_ViewModeChange , + ScriptEvent_SelectWeapon, + ScriptEvent_Deafened, + ScriptEvent_StanceChange, + ScriptEvent_CycleGrenade, + ScriptEvent_Use, + ScriptEvent_MeleeAttack, + ScriptEvent_PhysicalizeOnDemand, + ScriptEvent_PhysCollision, + ScriptEvent_FlashLightSwitch, + ScriptEvent_EnterWater, + ScriptEvent_CycleVehiclePos, + ScriptEvent_AllClear, + ScriptEvent_Expression, + ScriptEvent_InVehicleAnimation, + ScriptEvent_InVehicleAmmo, + ScriptEvent_ProcessCharacterEffects, + ScriptEvent_Jump, + }; + + class Entity + { + public: + Entity(); + virtual ~Entity(); + void SetEnabled(bool state); + bool IsEnabled(); + const String& GetName() const; + void SetName(const String& _name); + virtual void Update(float timeElapsed); + void Save(); + void SetPhysicsEntity(IPhysicsEntity* _phys); + void SetGraphicalEntity(IGraphicsEntity* _graph); + IPhysicsEntity* GetPhysicsEntity(); + IGraphicsEntity* GetGraphicalEntity(); + void CallScriptEvent(String eventName); + LifeCore::ITransform* GetTransform(); + void AddLogicEntity(GOCLogic* logic); + private: + IPhysicsEntity* physics; + IGraphicsEntity* graphics; + bool bPhysicalized; + LifeCore::ITransform transform; + String name; + bool bEnabled; + std::vector logic_components; + }; +} + +#endif // Entity_h__ \ No newline at end of file diff --git a/LifeGame/src/EntityFactory.h b/LifeGame/src/EntityFactory.h new file mode 100644 index 0000000..e42379d --- /dev/null +++ b/LifeGame/src/EntityFactory.h @@ -0,0 +1,125 @@ +#ifndef EntityFactory_h__ +#define EntityFactory_h__ + +#include "Singleton.h" +#include "Globals.h" +#include "XML.h" +#include "Game.h" + +namespace LifeGame +{ + class EntityFactory; + class EntityFactory : public Singleton + { + public: + LifeGame::Entity* BuildEntity(const String& path, Game* engine) + { + pugi::xml_document doc; + + //load file to memory + char* data; + LifeCore::IFile* file; + LifeGraphics::ResourceManager::me()->getFileSystem()->GetFile(path, &file); + LifeCore::IDataStreamPtr stm = file->OpenRead(); + data = new char[stm->Length()]; + stm->Read(data, stm->Length()); + doc.load_buffer(data, stm->Length()); + stm->Close(); + delete[] data; + + pugi::xml_node root = doc.child("Entity"); + pugi::xml_node game = root.child("Game"); + Entity* e; + + e = new Entity(); + + e->SetName(root.attribute("Name").value()); + + //if entity have graphics - load it + if(root.attribute("bGraphics").as_bool()) + { + pugi::xml_node g = root.child("Graphics"); + IGraphicsEntity* visual = new IGraphicsEntity(engine->GetGraphics()); + visual->Load(AS_STRING("root/Data/Objects/") + g.attribute("MeshFile").value()); + + pugi::xml_node mat = g.child("Materials"); + //load materials + int curId =0; + for (pugi::xml_attribute_iterator ait = mat.attributes_begin(); ait != mat.attributes_end(); ++ait) + { + if(ait->name() == AS_STRING("ID")) + { + curId = ait->as_int(); + } + else + { + if(ait->value() != AS_STRING("")) + { + // LifeGraphics::IMaterial* material = new LifeGraphics::IMaterial(); + // material->load(AS_STRING("root/Data/Materials/") + AS_STRING(ait->value()) + AS_STRING(".xml")); + //visual->pMesh->setMaterial(material, curId); + } + } + } + e->SetGraphicalEntity(visual); + } + + if(root.attribute("bPhysics").as_bool()) + { + pugi::xml_node physx = root.child("Physics"); + + IPhysicsEntity* phys = new IPhysicsEntity(e); + + //phys.append_attribute("sMeshName") = "Level2.ocf"; + if(physx.attribute("sMeshName").value() != AS_STRING("")) + { + //if has collision mesh + //read data + LifeCore::IFile* file; + LifeGraphics::ResourceManager::me()->getFileSystem()->GetFile(AS_STRING("root/Data/Objects/") + physx.attribute("sMeshName").value(), &file); + + LifeCore::IDataStreamPtr stm = file->OpenRead(); + char* data = new char[stm->Length()]; + stm->Read(data, stm->Length()); + stm.release(); + + CollisionMeshData* meshData = new CollisionMeshData(); + meshData->faceCount = (reinterpret_cast(data))[0]; + meshData->vertexCount = meshData->faceCount * 3; + meshData->indexCount = meshData->vertexCount; + meshData->SixteenBit = false; + meshData->vertices = reinterpret_cast(data + sizeof(int)); + meshData->indices = reinterpret_cast(data + sizeof(int) + sizeof(LifeMath::float3)* meshData->vertexCount); + + phys->CreateRigidBodyPhysics(LifeMath::float3(0.0f,0.0f,0.0f), NULL, physx.attribute("fMass").as_float(), meshData); + } + else + { + if(game.attribute("bPlayer").as_bool()) + { + phys->CreateCharacterPhysics(LifeMath::float3(0.0f,0.0f,0.0f), physx.attribute("fMass").as_float(),0.6f,1.8f); + } + else + { + //no collision mesh specified! Just use bounding box + phys->CreateRigidBodyPhysics(LifeMath::float3(0.0f,220.0f,0.0f), e->GetGraphicalEntity()->pMesh->getBoundingBox(), physx.attribute("fMass").as_float(), NULL); + } + } + + e->SetPhysicsEntity(phys); + } + return e; + } + Entity* CreatePlane(float width, float height, bool _physicalized, bool _static, Game* engine) + { + Entity* e = new Entity(); + + IGraphicsEntity* ge = new IGraphicsEntity(engine->GetGraphics()); + + return e; + } + }; +} + + +#endif // EntityFactory_h__ \ No newline at end of file diff --git a/LifeGame/src/GOCCameraFree.cpp b/LifeGame/src/GOCCameraFree.cpp new file mode 100644 index 0000000..a899955 --- /dev/null +++ b/LifeGame/src/GOCCameraFree.cpp @@ -0,0 +1,110 @@ +#include "GOCCameraFree.h" + +namespace LifeGame +{ + GOCCameraFree::GOCCameraFree() + :camera(nullptr){ + time_elapsed = 0.0f; + } + GOCCameraFree::~GOCCameraFree(){ + + } + void GOCCameraFree::SetCamera(GameCamera* cam){ + camera = cam; + } + void GOCCameraFree::Update(float timeElapsed){ + time_elapsed = timeElapsed; + if(!camera) + return; + } + void GOCCameraFree::OnAction(ActionId action, const ActionParam& param){ + if(action == "MoveForward"){ + if(camera){ + auto cam_dir = camera->getCam()->getDirection(); + auto dir = cam_dir * time_elapsed * 0.1f; + auto pos = camera->GetTransform()->GetPosition(); + pos += dir; + camera->GetTransform()->SetPosition(pos); + camera->getCam()->setDirection(pos + cam_dir); + } + }else if(action == "MoveBackward"){ + if(camera){ + auto cam_dir = camera->getCam()->getDirection(); + auto dir = cam_dir * -time_elapsed * 0.1f; + auto pos = camera->GetTransform()->GetPosition(); + pos += dir; + camera->GetTransform()->SetPosition(pos); + camera->getCam()->setDirection(pos + cam_dir); + } + }else if(action == "MoveLeft"){ + if(camera){ + auto cam_dir = camera->getCam()->getDirection(); + auto cam_right = camera->getCam()->getRight(); + auto dir = cam_right * -time_elapsed * 0.1f; + auto pos = camera->GetTransform()->GetPosition(); + pos += dir; + camera->GetTransform()->SetPosition(pos); + camera->getCam()->setDirection(pos + cam_dir); + } + }else if(action == "MoveRight"){ + if(camera){ + auto cam_dir = camera->getCam()->getDirection(); + auto cam_right = camera->getCam()->getRight(); + auto dir = cam_right * time_elapsed * 0.1f; + auto pos = camera->GetTransform()->GetPosition(); + pos += dir; + camera->GetTransform()->SetPosition(pos); + camera->getCam()->setDirection(pos + cam_dir); + } + }else if(action == "RotateYaw"){ + if(camera){ + LifeMath::float3 cam_up = camera->getCam()->getUp(); + LifeMath::float3 cam_dir = camera->getCam()->getDirection(); + LifeMath::float3 cam_right = camera->getCam()->getRight(); + + LifeMath::float3 global_up(0,1,0); + + LifeMath::Vec3Normalize(&cam_up); + LifeMath::Vec3Normalize(&cam_dir); + LifeMath::Vec3Normalize(&cam_right); + + float angle = (float)param.val.si32_val / -10000.0f; + LifeMath::Quaternion rotation; + LifeMath::QuatSetAxisAngle(global_up, angle, rotation); + LifeMath::float4x4 rotation_mat; + LifeMath::Mat4x4RotationQuaternion(rotation, rotation_mat); + + LifeMath::Vec3TransformCoordinate(cam_dir, rotation_mat); + LifeMath::Vec3TransformCoordinate(cam_right, rotation_mat); + LifeMath::Vec3TransformCoordinate(cam_up, rotation_mat); + + camera->getCam()->SetupBasis(camera->GetTransform()->GetPosition(), + cam_dir, cam_up, cam_right); + + } + }else if(action == "RotatePitch"){ + if(camera){ + /* LifeMath::float3 cam_up = camera->getCam()->getUp(); + LifeMath::float3 cam_dir = camera->getCam()->getDirection(); + LifeMath::float3 cam_right = camera->getCam()->getRight(); + + LifeMath::Vec3Normalize(&cam_up); + LifeMath::Vec3Normalize(&cam_dir); + LifeMath::Vec3Normalize(&cam_right); + + float angle = (float)param.val.si32_val / -10000.0f; + LifeMath::Quaternion rotation; + LifeMath::QuatSetAxisAngle(cam_right, angle, rotation); + LifeMath::float4x4 rotation_mat; + LifeMath::Mat4x4RotationQuaternion(rotation, rotation_mat); + + LifeMath::Vec3TransformCoordinate(cam_dir, rotation_mat); + LifeMath::Vec3TransformCoordinate(cam_up, rotation_mat); + + camera->getCam()->SetupBasis(camera->GetTransform()->GetPosition(), + cam_dir, cam_up, cam_right);*/ + + } + } + } +} \ No newline at end of file diff --git a/LifeGame/src/GOCCameraFree.h b/LifeGame/src/GOCCameraFree.h new file mode 100644 index 0000000..9c6c928 --- /dev/null +++ b/LifeGame/src/GOCCameraFree.h @@ -0,0 +1,23 @@ +#ifndef GOCCameraFree_h__ +#define GOCCameraFree_h__ + +#include "GOCLogic.h" +#include "GameCamera.h" +#include "IActionListener.h" + +namespace LifeGame +{ + class GOCCameraFree : public GOCLogic, public IActionListener + { + public: + GOCCameraFree(); + ~GOCCameraFree(); + void SetCamera(GameCamera* cam); + void Update(float timeElapsed); + void OnAction(ActionId action, const ActionParam& param) override; + private: + GameCamera* camera; + float time_elapsed; + }; +} +#endif // GOCCameraFree_h__ diff --git a/LifeGame/src/GOCLogic.h b/LifeGame/src/GOCLogic.h new file mode 100644 index 0000000..6e0be9c --- /dev/null +++ b/LifeGame/src/GOCLogic.h @@ -0,0 +1,15 @@ +#ifndef GOCLogic_h__ +#define GOCLogic_h__ + +#include "GOComponent.h" + +namespace LifeGame +{ + class GOCLogic : public GOComponent + { + public: + GOCLogic(){} + virtual ~GOCLogic(){} + }; +} +#endif // GOCLogic_h__ diff --git a/LifeGame/src/GOComponent.h b/LifeGame/src/GOComponent.h new file mode 100644 index 0000000..fc0e6f1 --- /dev/null +++ b/LifeGame/src/GOComponent.h @@ -0,0 +1,14 @@ +#ifndef GOComponent_h__ +#define GOComponent_h__ + +namespace LifeGame +{ + class GOComponent + { + public: + GOComponent(){} + virtual ~GOComponent(){} + virtual void Update(float timeElapsed){} + }; +} +#endif // GOComponent_h__ diff --git a/LifeGame/src/GUIOutput.h b/LifeGame/src/GUIOutput.h new file mode 100644 index 0000000..c6d78a3 --- /dev/null +++ b/LifeGame/src/GUIOutput.h @@ -0,0 +1,44 @@ +#ifndef GUIOutput_h__ +#define GUIOutput_h__ + +#include "IRenderOutput.h" +#include +#include +#include +#include "CImage.h" + +namespace LifeGame +{ + class GUIOutput : public LifeGraphics::IRenderOutput + { + public: + static GUIOutput* Create(LifeGUI::GUIRenderWindow* wnd, LifeGraphics::IRenderer* renderer){ + LifeGraphics::CustomSwapChain* sc = new LifeGraphics::CustomSwapChain(wnd->GetWidth(), wnd->GetHeight(), 1, renderer, FORMAT_R8G8B8A8_UNORM); + return new GUIOutput(wnd, sc); + } + private: + GUIOutput(LifeGUI::GUIRenderWindow* wnd, LifeGraphics::CustomSwapChain* sc) + :LifeGraphics::IRenderOutput(wnd, sc) + { + out_wnd = wnd; + out_sc = sc; + img = CImage::FromTexture(sc->GetBackBuffer(0)); + out_wnd->GetGUIWindow()->SetPicture(img); + wnd->GetGUIWindow()->Event_Resize += NewDelegate(this, &LifeGame::GUIOutput::OnResizeWND); + out_sc->Event_Resized += NewDelegate(this, &LifeGame::GUIOutput::OnResizeSC); + out_sc->Event_Presented += NewDelegate(this, &LifeGame::GUIOutput::OnResizeSC); + } + void OnResizeWND(LifeGUI::GUIWindow* wnd){ + out_sc->Resize(out_wnd->GetWidth(), out_wnd->GetHeight()); + } + void OnResizeSC(LifeGraphics::ISwapChain* sc){ + delete img; + img = CImage::FromTexture(sc->GetBackBuffer(0)); + out_wnd->GetGUIWindow()->SetPicture(img); + } + LifeGUI::GUIRenderWindow* out_wnd; + LifeGraphics::CustomSwapChain* out_sc; + CImage* img; + }; +} +#endif // GUIOutput_h__ diff --git a/LifeGame/src/Game.cpp b/LifeGame/src/Game.cpp new file mode 100644 index 0000000..d5f2988 --- /dev/null +++ b/LifeGame/src/Game.cpp @@ -0,0 +1,39 @@ +#include "Game.h" +#include + +namespace LifeGame +{ + + Game::Game() + :gameTime(0.0f), bRunning(false), fps(0), fcPos(0), frameCounter(0), customWindow(false) + { + SetResourcesPath(AS_STRING("D:\\development\\Programming\\Projects\\Data\\")); + pScriptSystem = new ScriptSystem(LifeGraphics::ResourceManager::me()->getFileSystem()); + + //register LOG + asIScriptEngine* engine = pScriptSystem->GetEngine(); + engine->RegisterObjectType("CLog", 0, asOBJ_REF | asOBJ_NOHANDLE); + engine->RegisterObjectMethod("CLog", "void Write(const string &in)", asMETHODPR(LifeCore::CLog, Write, (const String&), void), asCALL_THISCALL); + engine->RegisterObjectMethod("CLog", "void LogToConsole(bool)", asMETHODPR(LifeCore::CLog, LogToConsole, (bool), void), asCALL_THISCALL); + engine->RegisterGlobalProperty("CLog LOG", LifeCore::CLog::me()); + + //engine->RegisterObjectType("Entity", 0, asOBJ_REF); + + //engine->RegisterObjectType("Game", 0, asOBJ_REF | asOBJ_NOHANDLE); + //engine->RegisterObjectMethod("Game", "Entity@ GetPlayer()", asMETHODPR(Game, GetPlayer, (), Entity*), asCALL_THISCALL); + //engine->RegisterGlobalProperty("Game GameEngine", this); + //void CLog::LogToConsole(bool condition) + + //register game engine in script system + + //register graphics engine in script system + + //register physics engine in script system + + Script* config; + pScriptSystem->NewScript("root/Data/Scripts/Config.as", &config); + config->ExecuteGlobalFunction("void DoConfig()"); + + physicsWorld = new IPhysicsWorld(); + } +} diff --git a/LifeGame/src/Game.h b/LifeGame/src/Game.h new file mode 100644 index 0000000..155cd5b --- /dev/null +++ b/LifeGame/src/Game.h @@ -0,0 +1,167 @@ +#ifndef Game_h__ +#define Game_h__ + +#include "Entity.h" +#include "IPhysicsWorld.h" +#include "World.h" +#include "InputSystemOIS.h" +#include "script/ScriptSystem.h" +#include +#include +#include +#include "CGUIDataManager.h" + + +namespace LifeGame +{ + +#define MAX_FPS_COUNTER 100 + + class Game + { + protected: + LifeCore::DisplayMode* modes; + + IPhysicsWorld* physicsWorld; + LifeGraphics::LifeEngine* pGraphics; + LifeCore::IRenderWindow* output; + int mainWindow; + + World* gameWorld; + float gameTime; + + bool bRunning; + bool customWindow; + LifeCore::InputSystemOIS* input; + int frameCounter; + + ScriptSystem* pScriptSystem; + CGUIRenderer* gui_renderer; + LifeGUI::GUIManager* gui_manager; + + public: + + Game(); + + LifeCore::IRenderWindow* GetWindow() + { + return output; + } + void DrawDebug() + { + physicsWorld->CompileDebug(); + pGraphics->debugDrawer->Render(); + pGraphics->debugDrawer->Flush(); + } + LifeGraphics::LifeEngine* GetGraphics() + { + return pGraphics; + } + IPhysicsWorld* GetPhysics() + { + return physicsWorld; + } + void InitVideo(LifeGraphics::RENDERER_TYPE renderer, int width, int height) + { + pGraphics = new LifeGraphics::LifeEngine(); + pGraphics->Init(renderer, LifeGraphics::RenderParams(false, false)); + physicsWorld->SetDebugDrawer(pGraphics->debugDrawer); + LifeGUI::GUIManager::me()->Init(new CGUIRenderer(pGraphics->GetRenderer()), new CGUIDataManager()); + } + void SetResourcesPath(const String& path) + { + LifeGraphics::ResourceManager::me()->SetCentralDirectory(path); + } + void SetWindow(RenderWindowWin* window) + { + output = window; + } + void SetWindow(int width, int height) + { + output = new RenderWindowWin(); + output->SetSize(width, height); + } + void InitInput(int screenWidth, int screenHeight, LifeCore::IRenderWindow* wnd) + { + input = new LifeCore::InputSystemOIS(); + input->Init(wnd->GetWidth(), wnd->GetHeight(), false, wnd->GetWindowHandle()); + } + LifeCore::IInputSystem* GetInput() + { + return input; + } + int Run() + { + bRunning = true; + while(IsRunning()) + { + process(); + Sleep(0); + } + return 0; + } + bool IsRunning() + { + return bRunning; + } + void Exit() + { + bRunning = false; + } + + virtual ~Game() + { + SAFE_DELETE(physicsWorld); + } + World* GetWorld() + { + return gameWorld; + } + + void processWorld(World* world){ + input->Capture(); + + world->Update(0.001f); + world->Render(); + DrawDebug(); + + world->GetOutput()->GetSwapChain()->Present(); + world->GetOutput()->GetWindow()->Update(); + } + + void process() + { + + gameTime += pGraphics->GetTimeElapsed(); + + //pGraphics->GetRenderer()->ClearBuffers(CF_COLOR_TARGET | CF_DEPTH_BUFFER, 0xffff00, 1, 0); + + //stateManager.Update(); //to world + // stateManager.Render(); + + frameCounter++; + + avgFps[fcPos++] = 1.0f / GetGraphics()->GetTimeElapsed(); + + if(fcPos >= MAX_FPS_COUNTER) + { + fcPos = 0; + fps = 0.0f; + for(int i = 0; iGetPosition()); + } + void OnSetRotation() + { + } + public: + LifeGraphics::Camera* getCam() + { + return &cam; + } + GameCamera(float aspect) + { + cam.setAspect(aspect); + cam.setFov(3.1415f / 3.0f); + LifeCore::ITransform* t = GetTransform(); + t->Event_SetPosition += NewDelegate(this, &GameCamera::OnSetPosition); + t->Event_SetPosition += NewDelegate(this, &GameCamera::OnSetRotation); + } + }; +} + +#endif // EntityCamera_h__ \ No newline at end of file diff --git a/LifeGame/src/GameGlobals.h b/LifeGame/src/GameGlobals.h new file mode 100644 index 0000000..4bef651 --- /dev/null +++ b/LifeGame/src/GameGlobals.h @@ -0,0 +1,4 @@ +#ifndef GameGlobals_h__ +#define GameGlobals_h__ + +#endif // GameGlobals_h__ \ No newline at end of file diff --git a/LifeGame/src/Graphics.h b/LifeGame/src/Graphics.h new file mode 100644 index 0000000..086aa57 --- /dev/null +++ b/LifeGame/src/Graphics.h @@ -0,0 +1,22 @@ +#ifndef Graphics_h__ +#define Graphics_h__ + +#include +#include +#include +#include +#include +#include +#include +#include "IObject.h" +#include +#include +#include +#include +#include "vfs/pak/pak_io/PakFile.h" +#include "vfs/fs_win/IFSContentProvider.h" +#include "vfs/pak/IPakFileContentProvider.h" +#include "vfs/IFile.h" +#include "AnimationTrack.h" + +#endif // Graphics_h__ \ No newline at end of file diff --git a/LifeGame/src/IActionListener.h b/LifeGame/src/IActionListener.h new file mode 100644 index 0000000..158d043 --- /dev/null +++ b/LifeGame/src/IActionListener.h @@ -0,0 +1,15 @@ +#ifndef IActionListener_h__ +#define IActionListener_h__ + +#include "Action.h" + +namespace LifeGame +{ + class IActionListener + { + public: + virtual ~IActionListener(){} + virtual void OnAction(ActionId action, const ActionParam& param) = 0; + }; +} +#endif // IActionListener_h__ diff --git a/LifeGame/src/IActionProvider.h b/LifeGame/src/IActionProvider.h new file mode 100644 index 0000000..aa65465 --- /dev/null +++ b/LifeGame/src/IActionProvider.h @@ -0,0 +1,13 @@ +#ifndef IActionProvider_h__ +#define IActionProvider_h__ + +namespace LifeGame +{ + class IActionProvider + { + public: + virtual ~IActionProvider(){}; + virtual void Update(){} + }; +} +#endif // IActionProvider_h__ diff --git a/LifeGame/src/IGraphicsEntity.cpp b/LifeGame/src/IGraphicsEntity.cpp new file mode 100644 index 0000000..e0cfec4 --- /dev/null +++ b/LifeGame/src/IGraphicsEntity.cpp @@ -0,0 +1,36 @@ +#include "IGraphicsEntity.h" + +IGraphicsEntity::IGraphicsEntity(LifeGraphics::LifeEngine* engine) +{ + pEngine = engine; + pMesh = (LifeGraphics::IObjectMesh*)0; +} +IGraphicsEntity::~IGraphicsEntity() +{ +#ifdef _DEBUG + if(Unload()) + LifeCore::CLog::me()->Write(AS_STRING("Graphics entity unloaded")); +#endif // _DEBUG +} +bool IGraphicsEntity::Load(String path) +{ + pMesh = new LifeGraphics::IObjectMesh(path); + return true; +} +void IGraphicsEntity::SetBody(LifeGraphics::IObject* obj) +{ + pMesh = static_cast(obj); +} +LifeGraphics::IObject* IGraphicsEntity::GetBody() +{ + return pMesh; +} +bool IGraphicsEntity::Unload() +{ + if(pMesh) + { + SAFE_DELETE(pMesh); // fixup for unloading from engine!!! + return true; + } + return false; +} \ No newline at end of file diff --git a/LifeGame/src/IGraphicsEntity.h b/LifeGame/src/IGraphicsEntity.h new file mode 100644 index 0000000..5e90a4e --- /dev/null +++ b/LifeGame/src/IGraphicsEntity.h @@ -0,0 +1,19 @@ +#ifndef IGraphicsEntity_h__ +#define IGraphicsEntity_h__ + +#include "Graphics.h" + +class IGraphicsEntity +{ +public: + LifeGraphics::IObjectMesh* pMesh; + LifeGraphics::LifeEngine* pEngine; + IGraphicsEntity(LifeGraphics::LifeEngine* engine); + ~IGraphicsEntity(); + bool Load(String path); + bool Unload(); + void SetBody(LifeGraphics::IObject* obj); + LifeGraphics::IObject* GetBody(); +}; + +#endif // IGraphicsEntity_h__ \ No newline at end of file diff --git a/LifeGame/src/IPhysicsEntity.cpp b/LifeGame/src/IPhysicsEntity.cpp new file mode 100644 index 0000000..4e1fcb8 --- /dev/null +++ b/LifeGame/src/IPhysicsEntity.cpp @@ -0,0 +1,344 @@ +#include "IPhysicsEntity.h" +#include "Entity.h" +#include +#include "IPhysicsWorld.h" + +namespace LifeGame +{ + +static int ID = 0; + +void LifeMotionState::buildMatrix() +{ + bTransform.getOpenGLMatrix(reinterpret_cast(&mTransform)); + //setup new transform to owner + owner->GetTransform()->SetMatrix(mOffset1 * mTransform); +} + +void LifeMotionState::getWorldTransform(btTransform& worldTrans ) const +{ + LifeMath::float4x4 m2 = mOffset2 * owner->GetTransform()->GetMatrix(); + worldTrans.setFromOpenGLMatrix(reinterpret_cast(&m2)); +} + +void LifeMotionState::setWorldTransform(const LifeMath::float4x4& worldTrans) +{ + mTransform = mOffset1 * worldTrans; + bTransform.setFromOpenGLMatrix(reinterpret_cast(&mTransform)); +} + +IPhysicsEntity::IPhysicsEntity(Entity* _owner) + :data(0), collisionFlags(0), bGhost(false), prevPairCnt(0), curPairCnt(0) +{ + owner = _owner; + id = ID++; +} + +void IPhysicsEntity::EnableContactCallbacks(bool condition) +{ + int flag = body->getCollisionFlags() & (~btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK); + + body->setCollisionFlags(condition == true ? (flag | btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK) : flag); + + // body-> +} + +void IPhysicsEntity::SetLinearVelocity(const LifeMath::float3& velocity) +{ + if(body->getActivationState() == ISLAND_SLEEPING) + body->activate(true); + btVector3 vel; + vel.setX(velocity.X); + vel.setY(velocity.Y); + vel.setZ(velocity.Z); + + (static_cast(body))->setLinearVelocity(vel); + + //(Body as RigidBody).LinearVelocity = new Vector3(vel.X, vel.Y, vel.Z); +} + +bool FindCollider(int id, ContactPairInfo* pairs, int count) +{ + for(int i = 0; i< count; i++) + { + if(pairs[i].id == id) + return true; + } + return false; +} +void IPhysicsEntity::Update(IPhysicsWorld* pWorld) +{ + btOverlappingPairCache* worldPairCache = pWorld->GetBroadphaseInterface()->getOverlappingPairCache(); + + //find new collision pairs + if(bGhost) + { + //copy pairs to previous, find new pairs + if(curPairCnt) + { + memcpy(prevPairs, curPairs, curPairCnt * sizeof(ContactPairInfo)); + prevPairCnt = curPairCnt; + curPairCnt = 0; + } + + btPairCachingGhostObject* go = static_cast(body); + btBroadphasePairArray& pairArray = go->getOverlappingPairCache()->getOverlappingPairArray(); + + int numPairs = pairArray.size(); + //btManifoldArray manifolds; + + for(int i = 0; i < numPairs; i++) + { + //manifolds.clear(); + const btBroadphasePair& pair = pairArray[i]; + + btBroadphasePair* p = worldPairCache->findPair(pair.m_pProxy0, pair.m_pProxy1); + if(!p) + continue; + + btCollisionObject* obj1 = static_cast(p->m_pProxy0->m_clientObject); + btCollisionObject* obj2 = static_cast(p->m_pProxy1->m_clientObject); + + IPhysicsEntity* e1 = reinterpret_cast(obj1->getUserPointer()); + IPhysicsEntity* e2 = reinterpret_cast(obj2->getUserPointer()); + + if(!e1 || !e2) + continue; + + IPhysicsEntity* other = 0; + + if(e1->id == this->id) + other = e2; + else if(e2->id == this->id) + other = e1; + else + continue; + + if(!FindCollider(other->id, curPairs, curPairCnt)) + { + ContactPairInfo info; + info.id = other->id; + info.dataPtr = other; + curPairs[curPairCnt] = info; + curPairCnt++; + } + //p->m_pProxy0-> + //pair.m_pProxy0-> + } + + //first 2 "if" cases is just optimization + if(curPairCnt == 0 && prevPairCnt > 0) + { + for(int i = 0; i < prevPairCnt; i++) + { + //do "on dispose contact" + this->Event_CollisionDispose.Invoke((IPhysicsEntity*)prevPairs[i].dataPtr); + } + } + else if(prevPairCnt == 0 && curPairCnt > 0) //new contacts + { + for(int i = 0; i < curPairCnt; i++) + { + //do "on create contact" + this->Event_CollisionCreate.Invoke((IPhysicsEntity*)curPairs[i].dataPtr); + } + } + else //partial changes + { + //find new contacts + for(int i = 0; i < curPairCnt; i++) + { + const ContactPairInfo& pair = curPairs[i]; + + if(!FindCollider(pair.id, prevPairs, prevPairCnt)) + { + this->Event_CollisionCreate.Invoke((IPhysicsEntity*)pair.dataPtr); + } + } + + //find disposed contacts + for(int i = 0; i < prevPairCnt; i++) + { + const ContactPairInfo& pair = prevPairs[i]; + + if(!FindCollider(pair.id, curPairs, curPairCnt)) + { + this->Event_CollisionDispose.Invoke((IPhysicsEntity*)pair.dataPtr); + } + } + } + } + + /*if(curPairCnt) + { + prev + }*/ +} +void IPhysicsEntity::CreateTriggerPhysics(const LifeMath::float3& initialPos, float sx, float sy, float sz) +{ + bGhost = true; + mass = 0; + + data = 0; + LifeMath::float3 offset; + + LifeMath::float3 size = LifeMath::float3(sx, sy, sz); + offset = LifeMath::float3(0,0,0); + + Mat4x4Translation(offset, mOffset2); + Mat4x4Translation(offset * -1.0f, mOffset1); + + //shape = new btPairCachingGhostObject(); + shape = new btBoxShape(btVector3(sx / 2.0f, sy / 2.0f, sz / 2.0f)); + + btTransform transform; + transform.setIdentity(); + transform.setOrigin(btVector3(0.0f, 0.8f, 0.0f)); + + btVector3 inertia(0.0f, 0.0f, 0.0f); + collisionFlags = COL_STATIC; + + btTransform transform2; + transform2.setIdentity(); + transform2.setOrigin(btVector3(initialPos.X + offset.X, initialPos.Y + offset.Y, initialPos.Z + offset.Z)); + + state = new LifeMotionState(transform2, owner, mOffset1, mOffset2); + //btRigidBody::btRigidBodyConstructionInfo ci(mass, state, shape, inertia); + body = new btPairCachingGhostObject(); + body->setWorldTransform(transform2); + body->setCollisionShape(shape); + + body->setUserPointer(this); + // body = new btRigidBody(ci); +} + +void IPhysicsEntity::SetAngularFactor(const LifeMath::float3& velocity) +{ + btVector3 vel; + vel.setX(velocity.X); + vel.setY(velocity.Y); + vel.setZ(velocity.Z); + + (static_cast(body))->setAngularFactor(vel); +} + +void IPhysicsEntity::CreateCharacterPhysics(const LifeMath::float3& position, float _mass, float radius, float height) +{ + Mat4x4Translation(LifeMath::float3(0.0f,0.0f,0.0f), mOffset2); + Mat4x4Translation(LifeMath::float3(0.0f,0.0f,0.0f), mOffset1); + mass = _mass; + float capsuleHeight = height - radius * 2; + + btCompoundShape* characterShape = new btCompoundShape(true); + shape = characterShape; + //componentShapes.push_back(shape); + btCapsuleShape* capsule = new btCapsuleShape(radius,capsuleHeight); + componentShapes.push_back(capsule); + + btTransform transform; + transform.setIdentity(); + transform.setOrigin(btVector3(0.0f,height/2.0f, 0.0f)); + characterShape->addChildShape(transform, capsule); + + shape = characterShape; + + transform.setIdentity(); + transform.setOrigin(btVector3(0.0f, 0.0f, 0.0f)); + + btVector3 inertia(0.0f, 0.0f, 0.0f); + if(mass > 0.0f) + shape->calculateLocalInertia(mass, inertia); + + collisionFlags = COL_PLAYER; + + btTransform transform2; + transform2.setIdentity(); + transform2.setOrigin(btVector3(position.X, position.Y, position.Z)); + + state = new LifeMotionState(transform2, owner, mOffset1, mOffset2); + btRigidBody::btRigidBodyConstructionInfo ci(mass, state, shape, inertia); + body = new btRigidBody(ci); + body->setUserPointer(this); + + /*; + (Shell as CompoundShape).AddChildShape(Matrix.Translation(0,0.8f,0),new CapsuleShape(radius, capsuleHeight)); + + Vector3 inertia = new Vector3(); + Shell.CalculateLocalInertia(mass, out inertia); + + BulletSharp.MotionState state = new DefaultMotionState(Matrix.Identity); + RigidBodyConstructionInfo info = new RigidBodyConstructionInfo(mass, state, Shell, inertia); + Body = (CollisionObject) new RigidBody(info); + Body.UserObject = this.Owner; + + PhysicsSystem.Instance.Scene.g_world.AddRigidBody((RigidBody) Body);*/ +} + +void IPhysicsEntity::PhysicsGetOwnerTransform() +{ + m2 = mOffset2 * owner->GetTransform()->GetMatrix(); + bTransform.setFromOpenGLMatrix(reinterpret_cast(&m2)); + //body->activate(true); + body->setWorldTransform(bTransform); + body->setInterpolationWorldTransform(bTransform); +} + +//void IPhysicsEntity::CreateMeshPhysics(const LifeMath::float3& pos, float mass, ) + +void IPhysicsEntity::CreateRigidBodyPhysics(const LifeMath::float3& initialPos, const LifeCore::BoundingBox3D* bbox, float _mass, CollisionMeshData* _data) +{ + mass = _mass; + + data = _data; + LifeMath::float3 offset; + + if(!data) + { + LifeMath::float3 size = bbox->maximum() - bbox->minimum(); + offset = bbox->minimum() + size * 0.5f; + + Mat4x4Translation(offset, mOffset2); + Mat4x4Translation(offset * -1.0f, mOffset1); + + shape = new btBoxShape(btVector3(size.X / 2.0f, size.Y / 2.0f, size.Z / 2.0f)); + } + else + { + offset = LifeMath::float3(0.0f,0.0f,0.0f); + Mat4x4Translation(LifeMath::float3(0.0f,0.0f,0.0f), mOffset2); + Mat4x4Translation(LifeMath::float3(0.0f,0.0f,0.0f), mOffset1); + + btTriangleIndexVertexArray* arr = new btTriangleIndexVertexArray(data->faceCount,data->indices,sizeof(int) * 3,data->vertexCount, reinterpret_cast(data->vertices), sizeof(LifeMath::float3)); + shape = new btBvhTriangleMeshShape(arr, true, true); + } + + btTransform transform; + transform.setIdentity(); + transform.setOrigin(btVector3(0.0f, 0.8f, 0.0f)); + + btVector3 inertia(0.0f, 0.0f, 0.0f); + if(mass > 0.0f) + { + shape->calculateLocalInertia(mass, inertia); + collisionFlags = COL_DYNAMIC; + } + else + { + collisionFlags = COL_STATIC; + } + + btTransform transform2; + transform2.setIdentity(); + transform2.setOrigin(btVector3(initialPos.X + offset.X, initialPos.Y + offset.Y, initialPos.Z + offset.Z)); + + state = new LifeMotionState(transform2, owner, mOffset1, mOffset2); + btRigidBody::btRigidBodyConstructionInfo ci(mass, state, shape, inertia); + body = new btRigidBody(ci); + body->setUserPointer(this); + //body->setWorldTransform(transform2); + + /*; + + PhysicsSystem.Instance.Scene.g_world.AddRigidBody((RigidBody) Body);*/ +} +} diff --git a/LifeGame/src/IPhysicsEntity.h b/LifeGame/src/IPhysicsEntity.h new file mode 100644 index 0000000..2c6b3c8 --- /dev/null +++ b/LifeGame/src/IPhysicsEntity.h @@ -0,0 +1,151 @@ +#ifndef IPhysicsEntity_h__ +#define IPhysicsEntity_h__ + +#include "Graphics.h" +#include +#include +#include +#include +#include +#include +#include +#include "BulletLibs.h" +#include +#include + + +namespace LifeGame +{ + enum collisiontypes { + COL_NOTHING = 0, + COL_STATIC = 1 << 0, + COL_PLAYER = 1 << 1, + COL_DYNAMIC = 1 << 2, + COL_SENSOR = 1 <<3, + }; + + class Entity; + class CollisionMeshData + { + public: + + char* dataPtr; + int vertexCount; + int faceCount; + int indexCount; + LifeMath::float3* vertices; + int* indices; + bool SixteenBit; + + CollisionMeshData() + :dataPtr(0) + { + } + ~CollisionMeshData() + { + if(dataPtr) + { + delete[] dataPtr; + } + } + }; + + struct ContactPairInfo + { + void* dataPtr; + int id; + }; + + class LifeMotionState : public btMotionState + { + private: + btTransform bTransform; + LifeMath::float4x4 mTransform; + Entity* owner; + void buildMatrix(); + + public: + LifeMath::float4x4 mOffset1; + LifeMath::float4x4 mOffset2; + LifeMotionState(btTransform& initialTransform, Entity* _owner, LifeMath::float4x4& offset1, LifeMath::float4x4& offset2) + { + owner = _owner; + bTransform = initialTransform; + mOffset1 = offset1; + mOffset2 = offset2; + buildMatrix(); + } + ~LifeMotionState() + { + } + void getWorldTransform(btTransform& worldTrans ) const; + + void setWorldTransform(const btTransform& worldTrans) + { + bTransform = worldTrans; + buildMatrix(); + } + void setWorldTransform(const LifeMath::float4x4& worldTrans); + }; + + class IPhysicsWorld; + class IPhysicsEntity + { + private: + int id; + float mass; + bool bGhost; + CollisionMeshData* data; + btCollisionShape* shape; + btCollisionObject* body; + std::vector componentShapes; + + btTransform bTransform; + LifeMath::float4x4 m2; + + EVENT1(PhysicsEvent, void*); + PhysicsEvent Event_CollisionCreate; + PhysicsEvent Event_CollisionDispose; + + ContactPairInfo prevPairs[256]; + ContactPairInfo curPairs[256]; + int prevPairCnt; + int curPairCnt; + public: + short collisionFlags; + float GetMass() + { + return mass; + } + Entity* owner; + LifeMotionState* state; + LifeMath::float4x4 mOffset1; + LifeMath::float4x4 mOffset2; + void Update(IPhysicsWorld* pWorld); + bool IsStatic() + { + return mass <=0; + } + btCollisionObject* GetBody() + { + return body; + } + IPhysicsEntity(Entity* _owner); + void PhysicsGetOwnerTransform(); + + ~IPhysicsEntity() + { + if(data) + delete data; + } + void EnableContactCallbacks(bool condition); + void SetLinearVelocity(const LifeMath::float3& velocity); + void SetAngularFactor(const LifeMath::float3& velocity); + void CreateTriggerPhysics(const LifeMath::float3& initialPos, float sx, float sy, float sz); + + void CreateCharacterPhysics(const LifeMath::float3& position, float _mass, float radius, float height); + void CreateRigidBodyPhysics(const LifeMath::float3& initialPos, const LifeCore::BoundingBox3D* bbox, float _mass, CollisionMeshData* data); + }; +} + +#endif // IPhysicsEntity_h__ \ No newline at end of file diff --git a/LifeGame/src/IPhysicsWorld.h b/LifeGame/src/IPhysicsWorld.h new file mode 100644 index 0000000..2ca75c0 --- /dev/null +++ b/LifeGame/src/IPhysicsWorld.h @@ -0,0 +1,228 @@ +#ifndef IPhysicsWorld_h__ +#define IPhysicsWorld_h__ +#include +#include "BulletLibs.h" +#include "IPhysicsEntity.h" +#include + +#define MAX_DEBUG_LINE_CNT 1024 +#include +#include "debug/IDebugDrawer.h" + +namespace LifeGame +{ + class DebugDrawer : public btIDebugDraw + { + public: + LifeGraphics::DebugVertex lines[MAX_DEBUG_LINE_CNT * 2]; + int lineCount; + int dbgMode; + LifeGraphics::IDebugDrawer* debug; + + DebugDrawer(LifeGraphics::IDebugDrawer* dd) + :lineCount(0), dbgMode(DBG_DrawWireframe | DBG_DrawConstraints) + { + debug = dd; + } + ~DebugDrawer() + { + } + void drawLine(const btVector3& from,const btVector3& to,const btVector3& color) + { + LifeGraphics::DebugVertex v1; + v1.position = LifeMath::float3(from.x(), from.y(), from.z()); + v1.color = LifeMath::float3(color.x(), color.y(), color.z()); + + LifeGraphics::DebugVertex v2; + v2.position = LifeMath::float3(to.x(), to.y(), to.z()); + v2.color = LifeMath::float3(color.x(), color.y(), color.z()); + + debug->PushLine(v1,v2); + } + + void drawContactPoint(const btVector3& PointOnB,const btVector3& normalOnB,btScalar distance,int lifeTime,const btVector3& color) + { + } + + void reportErrorWarning(const char* warningString) + { + } + + void draw3dText(const btVector3& location,const char* textString) + { + } + + void setDebugMode(int debugMode) + { + dbgMode = debugMode; + } + + int getDebugMode() const + { + return dbgMode; + } + }; + + class IPhysicsWorld + { + private: + + btDiscreteDynamicsWorld* pWorld; + btBroadphaseInterface* pBroadphase; + btCollisionConfiguration* pCollisionConfiguration; + btConstraintSolver* pSolver; + btCollisionDispatcher* pDispatcher; + DebugDrawer* debugDraw; + std::vector entityCollection; + + //SortedList g_components; + + public: + float RayTest(const LifeMath::float3& from, const LifeMath::float3& to) + { + btVector3 f(from.X,from.Y, from.Z); + btVector3 t(to.X, to.Y, to.Z); + + btCollisionWorld::ClosestRayResultCallback ray(f, t); + + pWorld->rayTest(f, t, ray); + LifeMath::float3 pt(ray.m_hitPointWorld.getX(), ray.m_hitPointWorld.getY(), ray.m_hitPointWorld.getZ()); + return Vec3Distance(from, pt); + } + void ApplyForce(const LifeMath::float3& from, const LifeMath::float3& to, float power) + { + btVector3 f(from.X,from.Y, from.Z); + btVector3 t(to.X, to.Y, to.Z); + + btCollisionWorld::ClosestRayResultCallback ray(f, t); + + pWorld->rayTest(f, t, ray); + LifeMath::float3 pt(ray.m_hitPointWorld.getX(), ray.m_hitPointWorld.getY(), ray.m_hitPointWorld.getZ()); + if(!ray.m_collisionObject->isStaticObject()) + { + LifeMath::float3 force = to - from; + Vec3Normalize(&force); + force = force * power; + + btRigidBody* rb = static_cast(ray.m_collisionObject); + rb->activate(true); + // rb->applyCentralForce(btVector3(force.X, force.Y, force.Z)); + rb->applyForce(btVector3(force.X, force.Y, force.Z), ray.m_hitPointWorld); + } + } + float SphereTest(const LifeMath::float3& from, const LifeMath::float3& to, float radius) + { + btSphereShape CameraSphere(radius); + btTransform CameraFrom, CameraTo; + CameraFrom.setIdentity(); + CameraFrom.setOrigin(btVector3(from.X, from.Y, from.Z)); + CameraTo.setIdentity(); + CameraTo.setOrigin(btVector3(to.X, to.Y, to.Z)); + + btCollisionWorld::ClosestConvexResultCallback cb(CameraFrom.getOrigin(), CameraTo.getOrigin() ); + cb.m_collisionFilterMask = btBroadphaseProxy::StaticFilter; + cb.m_collisionFilterGroup = COL_SENSOR; + cb.m_collisionFilterMask = ~((int)COL_PLAYER); + + pWorld->convexSweepTest(&CameraSphere,CameraFrom,CameraTo,cb); + if (cb.hasHit()) + { + btScalar minFraction = cb.m_closestHitFraction; + float dist = Vec3Distance(from, to); + return dist * minFraction; + } + return 100500.0f; + } + btBroadphaseInterface* GetBroadphaseInterface() + { + return pBroadphase; + } + IPhysicsWorld() + :debugDraw(0) + { + LifeCore::CLog::me()->Write(AS_STRING("Loading physics engine...")); + pBroadphase = new btAxisSweep3(btVector3(-1000.0f,-1000.0f,-1000.0f), btVector3(1000.0f, 1000.0f, 1000.0f)); + //pBroadphase = new btDbvtBroadphase(); + pCollisionConfiguration = new btDefaultCollisionConfiguration(); + pSolver = new btSequentialImpulseConstraintSolver(); + pDispatcher = new btCollisionDispatcher(pCollisionConfiguration); + pWorld = new btDiscreteDynamicsWorld(pDispatcher, pBroadphase, pSolver, pCollisionConfiguration); + pWorld->setGravity(btVector3(0.0f, -9.8f, 0.0f)); + LifeCore::CLog::me()->Write(AS_STRING("Physics engine loaded")); + + //enable collisions for ghost objects + btGhostPairCallback* cb = new btGhostPairCallback(); + pWorld->getPairCache()->setInternalGhostPairCallback(cb); + + //test! create ground + btCollisionShape* groundShape = new btBoxShape(btVector3(100.0f,10.0f,100.0f)); + btRigidBody* ground; + + btTransform transform; + transform.setIdentity(); + transform.setOrigin(btVector3(0.0f, -10.0f, 0.0f)); + + btVector3 inertia = btVector3(0.0f, 0.0f, 0.0f); + //groundShape->calculateLocalInertia(0, inertia); + + btMotionState* state = new btDefaultMotionState(); + btRigidBody::btRigidBodyConstructionInfo ci(0.0f, state, groundShape, inertia); + ground = new btRigidBody(ci); + + ground->setWorldTransform(transform); + //pWorld->addRigidBody(ground); + } + void SetDebugDrawer(LifeGraphics::IDebugDrawer* _debug) + { + if(!debugDraw) + { + delete debugDraw; + } + debugDraw = new DebugDrawer(_debug); + pWorld->setDebugDrawer(debugDraw); + } + ~IPhysicsWorld() + { + } + void CompileDebug() + { + if(debugDraw) + pWorld->debugDrawWorld(); + } + void AddObject(IPhysicsEntity* ent) + { + short mask; + if(ent->collisionFlags == COL_STATIC) + { + mask = COL_DYNAMIC | COL_PLAYER | COL_SENSOR; + } + else + { + mask = COL_DYNAMIC | COL_PLAYER | COL_STATIC | COL_SENSOR; + } + + pWorld->addRigidBody(static_cast(ent->GetBody()),ent->collisionFlags, mask); + entityCollection.push_back(ent); + } + void Tick(float dt) + { + //update physics transform (in case when entity transform changed not by physics engine) + int cnt = entityCollection.size(); + for(int i = 0; i< cnt; i++) + { + IPhysicsEntity* e = entityCollection[i]; + + e->PhysicsGetOwnerTransform(); + e->state->setWorldTransform(e->mOffset2 * e->owner->GetTransform()->GetMatrix()); + + e->Update(this); + } + pWorld->stepSimulation(dt, 2); + } + protected: + private: + }; +} + + +#endif // IPhysicsWorld_h__ \ No newline at end of file diff --git a/LifeGame/src/IScriptEntity.h b/LifeGame/src/IScriptEntity.h new file mode 100644 index 0000000..c9bcf54 --- /dev/null +++ b/LifeGame/src/IScriptEntity.h @@ -0,0 +1,8 @@ +#ifndef IScriptEntity_h__ +#define IScriptEntity_h__ +#include + +class IScriptEntity +{ +}; +#endif // IScriptEntity_h__ \ No newline at end of file diff --git a/LifeGame/src/InputActionProvider.cpp b/LifeGame/src/InputActionProvider.cpp new file mode 100644 index 0000000..83d9c10 --- /dev/null +++ b/LifeGame/src/InputActionProvider.cpp @@ -0,0 +1,75 @@ +#include "InputActionProvider.h" + +namespace LifeGame +{ + InputActionProvider::InputActionProvider(){ + } + void InputActionProvider::Map(LifeCore::Key key, LifeCore::KeyAction key_action, const ActionId& game_action){ + bool found = false; + for(auto& map : key_mapping){ + if((map.key == key) && (map.key_action == key_action) && (map.game_action == game_action)){ + found = true; + break; + } + } + if(!found){ + key_mapping.push_back(KeyMapInfo(key, key_action, game_action)); + } + } + void InputActionProvider::Map(LifeCore::MouseAxis axis, const ActionId& action){ + bool found = false; + for(auto& map : axis_mapping){ + if(map.axis == axis && map.action == action){ + found = true; + break; + } + } + + if(!found){ + axis_mapping.push_back(AxisMapInfo(axis,action)); + } + } + void InputActionProvider::Unmap(LifeCore::Key key, LifeCore::KeyAction key_action, const ActionId& game_action){ + + } + void InputActionProvider::Update(){ + //update keyboard mapping + for(auto& map : key_mapping){ + if(map.key_action == LifeCore::KA_HOLD){ + if(GetInputSystem() != nullptr && GetInputSystem()->KeyPressed(map.key)){ + ActionParam param; + param.val.si32_val = decltype(param.val.si32_val)(map.key); + LifeGame::ActionSystem::me()->DoAction(map.game_action, param); + } + } + } + + + } + bool InputActionProvider::MouseMove( int absX, int relX, int absY, int relY){ + ActionParam param; + if(relX == 0 && relY == 0){ + return true; + } + //update mouse axis mapping + for(auto& map : axis_mapping){ + if(map.axis == LifeCore::MOUSE_AXIS_X && relX != 0){ + param.val.si32_val = relX; + char buf[256]; + sprintf(buf, "%i\n", relX); + OutputDebugString(buf); + LifeGame::ActionSystem::me()->DoAction(map.action, param); + }else if(map.axis == LifeCore::MOUSE_AXIS_Y && relY != 0){ + param.val.si32_val = relY; + LifeGame::ActionSystem::me()->DoAction(map.action, param); + } + } + return true; + } + bool InputActionProvider::KeyDown(LifeCore::Key key, unsigned int text, const std::string& str){ + return true; + } + bool InputActionProvider::KeyUp(LifeCore::Key key, unsigned int text, const std::string& str){ + return true; + } +} \ No newline at end of file diff --git a/LifeGame/src/InputActionProvider.h b/LifeGame/src/InputActionProvider.h new file mode 100644 index 0000000..4e8a136 --- /dev/null +++ b/LifeGame/src/InputActionProvider.h @@ -0,0 +1,52 @@ +#ifndef InputActionProvider_h__ +#define InputActionProvider_h__ + +#include "IActionProvider.h" +#include +#include "Action.h" +#include "ActionSystem.h" +#include + +#include + +namespace LifeGame +{ + struct KeyMapInfo{ + LifeCore::Key key; + LifeCore::KeyAction key_action; + ActionId game_action; + + KeyMapInfo(LifeCore::Key _key, LifeCore::KeyAction _key_action, const ActionId& _game_action){ + key = _key; + key_action = _key_action; + game_action = _game_action; + } + }; + + struct AxisMapInfo{ + LifeCore::MouseAxis axis; + ActionId action; + + AxisMapInfo(LifeCore::MouseAxis _axis, const ActionId& _game_action){ + axis = _axis; + action = _game_action; + } + }; + + class InputActionProvider final : public IActionProvider, public LifeCore::IInputListener + { + public: + InputActionProvider(); + void Map(LifeCore::Key key, LifeCore::KeyAction key_action, const ActionId& game_action); + void Map(LifeCore::MouseAxis axis, const ActionId& action); + void Unmap(LifeCore::Key key, LifeCore::KeyAction key_action, const ActionId& game_action); + void Update() override; + bool KeyDown(LifeCore::Key key, unsigned int text, const std::string& str) override; + bool KeyUp(LifeCore::Key key, unsigned int text, const std::string& str) override; + bool MouseMove( int absX, int relX, int absY, int relY) override; + private: + std::vector key_mapping; + std::vector axis_mapping; + }; +} +#endif // KeyboardActionProvider_h__ diff --git a/LifeGame/src/World.cpp b/LifeGame/src/World.cpp new file mode 100644 index 0000000..a30cf28 --- /dev/null +++ b/LifeGame/src/World.cpp @@ -0,0 +1,97 @@ +#include "World.h" +#include "Game.h" + +namespace LifeGame +{ + World::World(Game* parent, IPhysicsWorld* _physScene, LifeGraphics::IRenderOutput* output) + :main_camera(nullptr) + { + visualScene = new LifeGraphics::IScene(output->GetWindow()->GetWidth(), output->GetWindow()->GetHeight()); + physicsScene = _physScene; + render_out = output; + render_out->GetWindow()->Event_Resize += NewDelegate(this, &LifeGame::World::OnResize); + pGame = parent; + gui_manager = new LifeGUI::GUIOverlayManager(); + gui_manager->SetSize(output->GetWindow()->GetWidth(), output->GetWindow()->GetHeight()); + } + World::~World(){ + + } + void World::OnResize(LifeCore::IRenderWindow* sender){ + printf("Resize world!\n"); + GetGraphicsScene()->ResizeOutput(sender->GetWidth(), sender->GetHeight()); + GetGUI()->SetSize(sender->GetWidth(), sender->GetHeight()); + } + void World::Render(){ + pGame->GetGraphics()->RenderScene(visualScene, render_out); + gui_manager->Render(); + } + LifeGUI::GUIOverlayManager* World::GetGUI(){ + return gui_manager; + } + LifeGraphics::IScene* World::GetGraphicsScene(){ + return visualScene; + } + LifeGraphics::IRenderOutput* World::GetOutput(){ + return render_out; + } + void World::SetMainCamera(GameCamera* cam){ + for(auto s_cam : scene_cameras){ + if(s_cam == cam){ + main_camera = cam; + LOG_MESSAGE("Scene: Main camera selected!"); + + } + } + } + + void World::AddEntity(Entity* e) + { + if(e) + { + objects.push_back(e); + if(e->GetGraphicalEntity()) + { + visualScene->PushNode(e->GetGraphicalEntity()->pMesh); + } + if(e->GetPhysicsEntity()) + { + physicsScene->AddObject(e->GetPhysicsEntity()); + } + GameCamera* cam = dynamic_cast(e); + if(cam != nullptr){ + scene_cameras.push_back(cam); + LOG_MESSAGE("Scene: Camera added!"); + if(main_camera == nullptr){ + SetMainCamera(cam); + } + } + } + } + + void World::AddActionProvider(IActionProvider* provider){ + action_providers.push_back(provider); + } + + void World::Update(float timeElapsed) + { + if(main_camera != nullptr) + GetGraphicsScene()->SetMainCamera(main_camera->getCam()); + + for(EntityList::iterator it = objects.begin(); it != objects.end(); ++it) + { + Entity* e = (*it); + if(e->IsEnabled()) + { + e->Update(timeElapsed); + } + } + + for(auto provider : action_providers){ + provider->Update(); + } + + visualScene->Update(timeElapsed); + } +} + diff --git a/LifeGame/src/World.h b/LifeGame/src/World.h new file mode 100644 index 0000000..71dcca0 --- /dev/null +++ b/LifeGame/src/World.h @@ -0,0 +1,48 @@ +#ifndef World_h__ +#define World_h__ + +#include "Entity.h" +#include +#include "IScene.h" +#include "IPhysicsWorld.h" +#include +#include +#include "CGUIRenderer.h" +#include +#include +#include "IActionProvider.h" +#include "GameCamera.h" + +namespace LifeGame +{ + class Game; + class World : public LifeCore::IInputListener + { + public: + World(Game* parent, IPhysicsWorld* _physScene, LifeGraphics::IRenderOutput* output); + ~World(); + void AddEntity(Entity* e); + void Update(float timeElapsed); + void Render(); + LifeGUI::GUIOverlayManager* GetGUI(); + LifeGraphics::IRenderOutput* GetOutput(); + LifeGraphics::IScene* GetGraphicsScene(); + void AddActionProvider(IActionProvider* provider); + void SetMainCamera(GameCamera* cam); + private: + void OnResize(LifeCore::IRenderWindow* sender); + typedef std::vector EntityList; + EntityList objects; + LifeGraphics::IScene* visualScene; + IPhysicsWorld* physicsScene; + Game* pGame; + LifeGUI::GUIOverlayManager* gui_manager; + LifeGraphics::IRenderOutput* render_out; + std::vector action_providers; + std::vector scene_cameras; + GameCamera* main_camera; + }; +} + + +#endif // World_h__ \ No newline at end of file diff --git a/LifeGame/src/script/Script.cpp b/LifeGame/src/script/Script.cpp new file mode 100644 index 0000000..a415a62 --- /dev/null +++ b/LifeGame/src/script/Script.cpp @@ -0,0 +1,64 @@ +#include "script/Script.h" +#include +#include + +Script::Script() +{ + LOG_ERROR("Can not create new instance of Script! Script objects must be created ONLY inside ScriptSystem class!", "Script", "Script"); + throw std::bad_exception("Can not create Script instance outside of ScriptSystem class!"); +} +Script::Script(asIScriptEngine* engine, const String& module) + :pObj(0), pEngine(0), pContext(0) +{ + pEngine = engine; + name = module; + + pContext = engine->CreateContext(); + + Rebuild(); +} +void Script::ExecuteGlobalFunction(const String& decl) +{ + int id = pModule->GetFunctionIdByDecl(decl.c_str()); + if(id >= 0) + { + pContext->Prepare(id); + pContext->Execute(); + } +} +int Script::Rebuild() +{ + pModule = pEngine->GetModule(name.c_str()); + if(pModule == 0 || pEngine == 0) + { + LOG_ERROR("Can not create script object! Module us undefined", "Script", "Script"); + return SC_FAIL; + } + + int id = pModule->GetTypeIdByDecl(name.c_str()); + + if(pObj) + { + pObj->Release(); + } + pObj = (asIScriptObject*)pEngine->CreateScriptObject(id); + pObj->AddRef(); + + if(pContext == 0) + pContext = pEngine->CreateContext(); +} +Script::~Script() +{ + if(pContext) + { + pContext->Release(); + } + if(pObj) + { + pObj->Release(); + } +} +const String& Script::GetName() +{ + return name; +} \ No newline at end of file diff --git a/LifeGame/src/script/Script.h b/LifeGame/src/script/Script.h new file mode 100644 index 0000000..b451aef --- /dev/null +++ b/LifeGame/src/script/Script.h @@ -0,0 +1,25 @@ +#ifndef Script_h__ +#define Script_h__ + +#include +#include + +class Script +{ + friend class ScriptSystem; +public: + Script(); + ~Script(); + const String& GetName(); + int Rebuild(); + void ExecuteGlobalFunction(const String& decl); + //int GetObjectParam(const String& name); +private: + Script(asIScriptEngine* engine, const String& module); + String name; + asIScriptEngine* pEngine; + asIScriptContext* pContext; + asIScriptObject* pObj; + asIScriptModule* pModule; +}; +#endif // Script_h__ \ No newline at end of file diff --git a/LifeGame/src/script/ScriptSystem.cpp b/LifeGame/src/script/ScriptSystem.cpp new file mode 100644 index 0000000..7de4bf5 --- /dev/null +++ b/LifeGame/src/script/ScriptSystem.cpp @@ -0,0 +1,105 @@ +#include "script/ScriptSystem.h" +#include +#include + +ScriptSystem::ScriptSystem(LifeCore::IFileSystem* fileSystem) +{ + pFs = fileSystem; + + pEngine = asCreateScriptEngine(ANGELSCRIPT_VERSION); + RegisterStdString(pEngine); + + int r; + //register ScriptSystem + r = pEngine->RegisterObjectType("ScriptSystem", 0, asOBJ_REF | asOBJ_NOHANDLE); + if(r < 0) + { + printf("FUCK!"); + } + r = pEngine->RegisterObjectMethod("ScriptSystem", "int ReloadScript(const string &in)", asMETHODPR(ScriptSystem, ReloadScript, (const String&), int), asCALL_THISCALL); + if(r < 0) + { + printf("FUCK!"); + } + r = pEngine->RegisterGlobalProperty("ScriptSystem ScriptEngine", this); + if(r < 0) + { + printf("FUCK!"); + } +} +ScriptSystem::~ScriptSystem() +{ + for(ScriptVectorIterator it = scripts.begin(); it != scripts.end(); ++it) + { + delete *it; + } +} +int ScriptSystem::ReloadScript(const String& path) +{ + LifeCore::IFile* scriptFile; + if(pFs->GetFile(path, &scriptFile) != SC_OK) + { + LOG_ERROR("Can't load script! Invalid file specified.", "ScriptSystem", "LoadScript"); + return SC_FAIL; + } + + LifeCore::IDataStreamPtr stm = scriptFile->OpenRead(); + + String moduleName = GetFileNameWithoutExtention(path); + + asIScriptModule* pModule; + pModule = pEngine->GetModule(moduleName.c_str()); + + CScriptBuilder builder; + int r = builder.StartNewModule(pEngine, moduleName.c_str()); + + //read script text from file + char* buffer = new char[stm->Length() + 1]; + //+1 just to make sure that everything will go fine! Because file many not contain null terminating character! + buffer[stm->Length()] = '\0'; + stm->Read(buffer, stm->Length()); + stm.release(); + + //build new script module + r = builder.AddSectionFromMemory(buffer); + r = builder.BuildModule(); + + //if module is loaded not the first time - we have to rebuild all objects, created from this module + if(pModule != 0) + { + for(ScriptVectorIterator it = scripts.begin(); it != scripts.end(); ++it) + { + if((*it)->GetName() == moduleName) + { + (*it)->Rebuild(); + } + } + } + return SC_OK; +} + +int ScriptSystem::NewScript(const String& file, Script** output) +{ + if(pEngine == 0 || file == "") + return SC_FAIL; + + String module = GetFileNameWithoutExtention(file); + asIScriptModule* pModule; + pModule = pEngine->GetModule(module.c_str()); + + if(pModule == 0) + { + ReloadScript(file); + } + + Script* script = new Script(pEngine, module); + scripts.push_back(script); + *output = script; + + return SC_OK; +} + +asIScriptEngine* ScriptSystem::GetEngine() +{ + return pEngine; +} \ No newline at end of file diff --git a/LifeGame/src/script/ScriptSystem.h b/LifeGame/src/script/ScriptSystem.h new file mode 100644 index 0000000..e82df30 --- /dev/null +++ b/LifeGame/src/script/ScriptSystem.h @@ -0,0 +1,26 @@ +#ifndef ScriptSystem_h__ +#define ScriptSystem_h__ + +#include +#include +#include "script/Script.h" +#include +#include +#include + +class ScriptSystem +{ +public: + ScriptSystem(LifeCore::IFileSystem* fileSystem); + ~ScriptSystem(); + asIScriptEngine* GetEngine(); + int ReloadScript(const String& path); + int NewScript(const String& file, Script** output); +private: + LifeCore::IFileSystem* pFs; + asIScriptEngine* pEngine; + typedef std::vector ScriptVector; + typedef std::vector::iterator ScriptVectorIterator; + ScriptVector scripts; +}; +#endif // ScriptSystem_h__ \ No newline at end of file diff --git a/LifeGraphics/AnimationController.h b/LifeGraphics/AnimationController.h new file mode 100644 index 0000000..156414d --- /dev/null +++ b/LifeGraphics/AnimationController.h @@ -0,0 +1,118 @@ +#ifndef AnimationController_h__ +#define AnimationController_h__ + +#include +#include +#include "AnimationTrack.h" +#include "Skeleton.h" +#include "LString.h" +namespace LifeGraphics +{ + class AnimationController + { + public: + float fadeSpeed; + std::map tracks; + int trackCount; + Skeleton* skeleton; + int lastId; + std::vector plainList; + + void CrossFade(String name) + { + AnimationTrack* t = tracks[name]; + t->Weight += fadeSpeed; + if (t->Weight > 1.0f) t->Weight = 1.0f; + + for(std::map::iterator it = tracks.begin(); it != tracks.end(); it++) + { + AnimationTrack* t2 = it->second; + if (t2->Layer == t->Layer && t2->Name != t->Name) + { + t2->Weight -= fadeSpeed; + if (t2->Weight < 0) t2->Weight = 0; + } + } + } + + AnimationController() + :fadeSpeed(1.0f/20.0f), trackCount(0),lastId(0) + { + } + + void SetState(const String& stateName) + { + } + /// + /// Add animatrion track to the current animation controller. + /// + /// Animation track to add. + void AddTrack(AnimationTrack* track) + { + track->ID = lastId++; + tracks[track->Name] = track; + trackCount++; + } + + void SetTrackWeight(String track, float weight) + { + tracks[track]->Weight = weight; + } + + void LerpTransform(BoneTransform* left, const BoneTransform* right, float amount, BoneTransform* result) + { + if (amount == 0) + { + result->Rotation = left->Rotation; + result->Translation = left->Translation; + result->boneId = left->boneId; + return; + } + else if (amount == 1.0f) + { + result->Rotation = right->Rotation; + result->Translation = right->Translation; + result->boneId = right->boneId; + return; + } + + QuatLerp(left->Rotation, right->Rotation, amount, result->Rotation); + Vec3Lerp(left->Translation, right->Translation, amount, result->Translation); + result->boneId = left->boneId; + } + + void AdvanceTime(float dt) + { + for(std::map::iterator it = tracks.begin(); it != tracks.end(); it++) + { + AnimationTrack* track = it->second; + track->AdvanceTime(dt); + + const BoneTransform* transform = track->GetTransform(); + + for (int i = 0; i < skeleton->BonesCount; i++) + { + for (int j = 0; j < track->Frames[0].boneCount; j++) + { + if (transform[j].boneId == i) + { + Bone* bone = skeleton->GetBone(i); + BoneTransform lt = bone->LocalTransform; + LerpTransform(<, &transform[j], track->Weight, &bone->LocalTransform); + } + } + } + } + + skeleton->UpdateTransform(); + } + /// + /// Use to set up mesh into initial pose + /// + void Reset() + { + tracks[0]->Reset(); + } + }; +} +#endif // AnimationController_h__ \ No newline at end of file diff --git a/LifeGraphics/AnimationFrame.h b/LifeGraphics/AnimationFrame.h new file mode 100644 index 0000000..03b3e4f --- /dev/null +++ b/LifeGraphics/AnimationFrame.h @@ -0,0 +1,39 @@ +#ifndef AnimationFrame_h__ +#define AnimationFrame_h__ +#include +#include "BoneTransform.h" + +namespace LifeGraphics +{ + struct AnimationFrame + { + int name; + float time; + int boneCount; + std::vector bonesTransformations; + AnimationFrame() + { + } + void getTransformations(BoneTransform* transform) + { + for(int i = 0; i < boneCount; i++) + transform[i] = bonesTransformations[i]; + } + BoneTransform getTransform(int id) + { + return bonesTransformations[id]; + } + void addBoneTransform(BoneTransform& tr) + { + bonesTransformations.push_back(tr); + boneCount++; + } + AnimationFrame(float frameTime) + :boneCount(0) + { + time = frameTime; + name = -1; + } + }; +} +#endif // AnimationFrame_h__ \ No newline at end of file diff --git a/LifeGraphics/AnimationTrack.h b/LifeGraphics/AnimationTrack.h new file mode 100644 index 0000000..ee69d9e --- /dev/null +++ b/LifeGraphics/AnimationTrack.h @@ -0,0 +1,165 @@ +#ifndef AnimationTrack_h__ +#define AnimationTrack_h__ + +#include "AnimationFrame.h" +#include "LString.h" +#include "math/LifeMath.h" + +namespace LifeGraphics +{ + class AnimationTrack + { + public: + String Name; + int Layer; + AnimationFrame* Frames; + BoneTransform* transformations; + int FramesCount; + int ID; + int prev; + float Weight; + bool Enabled; + float TrackTime; + bool Loop; + float TrackPosition; + bool Playing; + + AnimationTrack(int framesCount) + :Layer(0), ID(0), Weight(1.0f),TrackPosition(0.0f), prev(0) + { + Frames = new AnimationFrame[framesCount]; + transformations = new BoneTransform[framesCount]; + FramesCount = framesCount; + } + + ~AnimationTrack() + { + if(Frames) + delete[] Frames; + if(transformations) + delete[] transformations; + } + + void setFrame(AnimationFrame& frame, int id) + { + Frames[id] = frame; + } + + const BoneTransform* GetTransform() + { + /*if (TrackPosition > Frames[FramesCount - 1].time) + if (Loop) + TrackPosition = 0.0f; + else + TrackPosition = Frames[FramesCount - 1].time;*/ + //if(TrackPosition < 0.0f || TrackPosition > Frames[FramesCount - 1].time) + //printf("OMFGNAH!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + + for (int i = 0; i < FramesCount; i++) + { + if (Frames[i].time == TrackPosition) + { + Frames[i].getTransformations(transformations); + return transformations; + } + + int next = i + 1; + if(next >= FramesCount) + next = 0; + + if (TrackPosition > Frames[i].time && TrackPosition < Frames[next].time) + { + float amount = (TrackPosition - Frames[i].time) / (Frames[next].time - Frames[i].time); + InterpolateFrames(Frames + i, Frames + (next), amount); + return transformations; + } + } + return transformations; + } + + void InterpolateFrames(AnimationFrame* start, AnimationFrame* end, float amount) + { + for (int i = 0; i < start->boneCount; i++) + { + BoneTransform newTransform; + + //BoneTransform tr1 = start->getTransform(i); + BoneTransform tr2 = end->getTransform(i); + + /* QuatSLerp(tr1.Rotation, tr2.Rotation, amount, newTransform.Rotation); + Vec3Lerp(tr1.Translation, tr2.Translation, amount, newTransform.Translation); + + if(amount < 0.0f | amount > 1.0f) + { + printf("PIZDEC!"); + } + + printf("Select frame: %i, Frame time: %f\n", end->name, end->time); + */ + newTransform.boneId = tr2.boneId; + + newTransform.Translation = tr2.Translation; + newTransform.Rotation = tr2.Rotation; + transformations[i] = newTransform; + } + } + + void AdvanceTime(float dt) + { + if (Playing && Weight >0) + { + TrackPosition += dt; + // TrackPosition = Frames[1].time; + //printf("Frame one id: %i\n", Frames[1].name); + if (TrackPosition > Frames[FramesCount - 1].time) + { + if (Loop) + { + //DoEvents(prevPos, TrackTime); + TrackPosition = TrackPosition - Frames[FramesCount - 1].time; + //OnTrackEnd(); + //OnTrackStart(); + // + } + else + { + TrackPosition = TrackTime; + //OnTrackEnd(); + Playing = false; + } + } + if (TrackPosition < 0) + { + TrackPosition = 0; + // OnTrackStart(); + } + if (TrackPosition == 0) + { + //OnTrackStart(); + } + else + { + //DoEvents(prevPos, TrackPosition); + } + } + } + + void Stop() + { + Playing = false; + } + + void Play() + { + if (TrackPosition >= TrackTime) + TrackPosition = 0; + Playing = true; + } + + void Reset() + { + TrackPosition = 0; + } + }; +} +#endif // AnimationTrack_h__ \ No newline at end of file diff --git a/LifeGraphics/Bone.h b/LifeGraphics/Bone.h new file mode 100644 index 0000000..0b41000 --- /dev/null +++ b/LifeGraphics/Bone.h @@ -0,0 +1,47 @@ +#ifndef Bone_h__ +#define Bone_h__ + +#include +#include +#include "BoneTransform.h" +#include + +namespace LifeGraphics +{ + class Bone + { + public: + BoneTransform BaseTransform; + BoneTransform LocalTransform; + BoneTransform InitialTransform; + //public BoneTransform AbsoluteTransform = new BoneTransform(); + int ID; + Bone* Parent; + std::vector Childs; + String BoneName; + + LifeMath::float4x4 TransformMatrix; + LifeMath::float4x4 AbsoluteMatrix; + int ParentId; + + void AddChild(Bone* child) + { + Childs.push_back(child); + } + void clearChilds() + { + Childs.clear(); + } + void Update() + { + } + Bone(int id, const String& name, int parentId) + :ID(-1), Parent(0) + { + BoneName = name; + ID = id; + ParentId = parentId; + } + }; +} +#endif // Bone_h__ \ No newline at end of file diff --git a/LifeGraphics/BoneTransform.h b/LifeGraphics/BoneTransform.h new file mode 100644 index 0000000..4735266 --- /dev/null +++ b/LifeGraphics/BoneTransform.h @@ -0,0 +1,65 @@ +#ifndef BoneTransform_h__ +#define BoneTransform_h__ +#include "math/LifeMath.h" + +namespace LifeGraphics +{ + struct BoneTransform + { + public: + int boneId; + LifeMath::float3 Translation; + LifeMath::Quaternion Rotation; + + BoneTransform() + :boneId(0) + { + } + + BoneTransform operator =(const BoneTransform& right) + { + boneId = right.boneId; + Translation = right.Translation; + Rotation = right.Rotation; + return *this; + } + + BoneTransform operator *(const BoneTransform& right) + { + BoneTransform bt; + bt.Translation = Translation + right.Translation; + bt.Rotation = Rotation * right.Rotation; + return bt; + } + + LifeMath::float4x4 GetMatrix() + { + LifeMath::float4x4 rot; + LifeMath::float4x4 trans; + LifeMath::Mat4x4RotationQuaternion(Rotation, rot); + LifeMath::Mat4x4Translation(Translation, trans); + return rot*trans; + } + + LifeMath::float4x4 GetMatrixInvert() + { + LifeMath::float4x4 rot; + LifeMath::float4x4 trans; + LifeMath::Mat4x4RotationQuaternion(Rotation, rot); + LifeMath::Mat4x4Translation(Translation, trans); + LifeMath::float4x4 transform = rot * trans; + transform = LifeMath::Mat4x4Invert(&transform); + return transform; + } + + BoneTransform GetInverted() + { + BoneTransform tr; + tr.Translation = -this->Translation; + tr.Rotation = this->Rotation; + QuatInvert(tr.Rotation); + return tr; + } + }; +} +#endif // BoneTransform_h__ \ No newline at end of file diff --git a/LifeGraphics/BoundingSphere.h b/LifeGraphics/BoundingSphere.h new file mode 100644 index 0000000..3a10733 --- /dev/null +++ b/LifeGraphics/BoundingSphere.h @@ -0,0 +1,19 @@ +#ifndef BoundingSphere_h__ +#define BoundingSphere_h__ +#include "math/LifeMath.h" + +namespace LifeGraphics +{ + class BoundingSphere + { + public: + BoundingSphere() + :center(0.0f, 0.0f, 0.0f), radius(20.0f) + { + } + ~BoundingSphere(){} + float radius; + LifeMath::float3 center; + }; +} +#endif // BoundingSphere_h__ \ No newline at end of file diff --git a/LifeGraphics/CG.cpp b/LifeGraphics/CG.cpp new file mode 100644 index 0000000..9a1d8ad --- /dev/null +++ b/LifeGraphics/CG.cpp @@ -0,0 +1,24 @@ +#include "CG.h" + +namespace LifeGraphics +{ + IRenderer* CG::GetRenderer() + { + return g_Renderer; + } + void CG::SetRenderer(IRenderer* renderer) + { + g_Renderer = renderer; + } + CG::CG() + :g_Renderer(0) + { + } + CG::~CG() + { + g_Renderer = NULL; + } + void CG::initialize() + { + } +} \ No newline at end of file diff --git a/LifeGraphics/CG.h b/LifeGraphics/CG.h new file mode 100644 index 0000000..c1087e4 --- /dev/null +++ b/LifeGraphics/CG.h @@ -0,0 +1,23 @@ +#ifndef CG_H__ +#define CG_H__ + +#include "render/IRenderer.h" +#include "Singleton.h" + +namespace LifeGraphics +{ + class CG; + + class CG : public Singleton + { + private: + IRenderer* g_Renderer; + public: + CG(); + ~CG(); + void initialize(); + void SetRenderer(IRenderer* renderer); + IRenderer* GetRenderer(); + }; +} +#endif \ No newline at end of file diff --git a/LifeGraphics/Camera.cpp b/LifeGraphics/Camera.cpp new file mode 100644 index 0000000..4cdca8e --- /dev/null +++ b/LifeGraphics/Camera.cpp @@ -0,0 +1,28 @@ +#include "camera/Camera.h" + +////////////////////////////////////////////////////////////////////////// + +namespace LifeGraphics +{ + Camera::Camera() + { + freeCam = true; + isViewValid = false; + isProjValid = false; + fDir = LifeMath::float3(0, 0, -1); + fov = 3.1415926535f / 4.0f; + nearPlane = 0.1f; + farPlane = 400.0f; + viewAspect = 1.0f; + eye = LifeMath::float3(0, 0, 0); + up = LifeMath::float3(0, 1, 0); + at = LifeMath::float3(0, 0, 1); + right = LifeMath::float3(1, 0, 0); + Mat4x4LookAtLH(eye, at, up, viewMatrix); + Mat4x4PerspectiveFOV(fov, viewAspect, nearPlane, farPlane, projectionMatrix); + frustum = Frustum(viewMatrix * projectionMatrix); + } + Camera::~Camera() + { + } +} \ No newline at end of file diff --git a/LifeGraphics/CustomSwapChain.cpp b/LifeGraphics/CustomSwapChain.cpp new file mode 100644 index 0000000..4e4a066 --- /dev/null +++ b/LifeGraphics/CustomSwapChain.cpp @@ -0,0 +1,45 @@ +#include "CustomSwapChain.h" + +namespace LifeGraphics +{ + CustomSwapChain::CustomSwapChain(uint16 width, uint16 height, uint16 buffer_cnt, LifeGraphics::IRenderer* host, IDataFormat format) + :back_buffer(nullptr), depth_buffer(nullptr){ + if(!host){ + sc_width = sc_height = sc_buffers = 0; + return; + } + sc_width = width; + sc_height = height; + sc_buffers = buffer_cnt; + sc_format = format; + sc_renderer = host; + RebuildBuffers(); + } + LifeGraphics::ITexture2D* CustomSwapChain::GetBackBuffer(uint8 id){ + return back_buffer; + } + LifeGraphics::ITexture2D* CustomSwapChain::GetDepthBuffer(uint8 id){ + return depth_buffer; + } + void CustomSwapChain::Resize(uint16 width, uint16 height){ + if(width != sc_width || height != sc_height){ + sc_width = width; + sc_height = height; + RebuildBuffers(); + Event_Resized.Invoke(this); + } + } + void CustomSwapChain::Present(){ + Event_Presented.Invoke(this); + } + void CustomSwapChain::RebuildBuffers(){ + if(back_buffer) + back_buffer->Release(); + + if(depth_buffer) + depth_buffer->Release(); + + back_buffer = sc_renderer->CreateRenderTarget(sc_width, sc_height, sc_format); + depth_buffer = sc_renderer->CreateDepthTarget(sc_width, sc_height, FORMAT_D24_UNORM_S8_UINT); + } +} \ No newline at end of file diff --git a/LifeGraphics/CustomSwapChain.h b/LifeGraphics/CustomSwapChain.h new file mode 100644 index 0000000..284e6ca --- /dev/null +++ b/LifeGraphics/CustomSwapChain.h @@ -0,0 +1,28 @@ +#ifndef CustomSwapChain_h__ +#define CustomSwapChain_h__ + +#include "ISwapChain.h" +#include "render/IRenderer.h" + +namespace LifeGraphics +{ + class CustomSwapChain : public ISwapChain + { + public: + CustomSwapChain(uint16 width, uint16 height, uint16 buffer_cnt, LifeGraphics::IRenderer* host, IDataFormat format); + LifeGraphics::ITexture2D* GetBackBuffer(uint8 id); + LifeGraphics::ITexture2D* GetDepthBuffer(uint8 id); + void Resize(uint16 width, uint16 height); + void Present(); + private: + void RebuildBuffers(); + uint16 sc_width; + uint16 sc_height; + uint16 sc_buffers; + IDataFormat sc_format; + LifeGraphics::IRenderer* sc_renderer; + LifeGraphics::ITexture2D* back_buffer; + LifeGraphics::ITexture2D* depth_buffer; + }; +} +#endif // CustomSwapChain_h__ diff --git a/LifeGraphics/DayTime.h b/LifeGraphics/DayTime.h new file mode 100644 index 0000000..2f52145 --- /dev/null +++ b/LifeGraphics/DayTime.h @@ -0,0 +1,137 @@ +#ifndef DayTime_h__ +#define DayTime_h__ + +class DayTime +{ +public: + static const int MaxSeconds = 24 * 3600; + int Hours, Minutes, Seconds; + + DayTime(int hours, int minutes, int seconds) + { + Hours = hours; + Minutes = minutes; + Seconds = seconds; + } + + DayTime() + { + Hours = 0; + Minutes = 0; + Seconds = 0; + } + + DayTime(int time) + { + int dc1 = time % 3600; + int dc2 = dc1 % 60; + Hours = ((time - dc1) / 3600) % 24; + Minutes = (dc1 - dc2) / 60; + Seconds = dc2; + } + + int ToSeconds() const + { + return Hours * 3600 + Minutes * 60 + Seconds; + } + + bool operator >=(const DayTime& right) const + { + return ToSeconds() >= right.ToSeconds(); + } + + bool operator <=(const DayTime& right) const + { + return ToSeconds() <= right.ToSeconds(); + } + + bool operator >(const DayTime& right) const + { + return ToSeconds() > right.ToSeconds(); + } + + bool operator <(const DayTime& right) const + { + return ToSeconds() < right.ToSeconds(); + } + + bool operator !=(const DayTime& right) const + { + return ToSeconds() != right.ToSeconds(); + } + + bool operator ==(const DayTime& right) const + { + return ToSeconds() == right.ToSeconds(); + } + + DayTime operator +(const DayTime& right) const + { + int sum = ToSeconds() + right.ToSeconds(); + return DayTime(sum); + } + + DayTime operator +(int time) const + { + int sum = ToSeconds() + time; + return DayTime(sum); + } + + DayTime operator -(const DayTime& right) const + { + int t1 = ToSeconds(); + int t2 = right.ToSeconds(); + int s = 0; + if (t1 < t2) + { + s = MaxSeconds - t2 + t1; + } + else + s = t1 - t2; + return DayTime(s); + } + + DayTime operator -(int time) + { + int t1 = ToSeconds(); + int t2 = time; + int s = 0; + if (t1 < t2) + { + s = MaxSeconds - t2 + t1; + } + else + s = t1 - t2; + return DayTime(s); + } + + DayTime operator /(int divider) const + { + return DayTime(ToSeconds() / divider); + } + + DayTime operator /(const DayTime& divider) const + { + return DayTime(ToSeconds() / divider.ToSeconds()); + } + + void Add(const DayTime& time) + { + SetTime(ToSeconds() + time.ToSeconds()); + } + + void Add(int time) + { + SetTime(ToSeconds() + time); + } + + void SetTime(int time) + { + int dc1 = time % 3600; + int dc2 = dc1 % 60; + Hours = ((time - dc1) / 3600) % 24; + Minutes = (dc1 - dc2) / 60; + Seconds = dc2; + } +}; +#endif // DayTime_h__ \ No newline at end of file diff --git a/LifeGraphics/GeometrySubset.h b/LifeGraphics/GeometrySubset.h new file mode 100644 index 0000000..c4da9b7 --- /dev/null +++ b/LifeGraphics/GeometrySubset.h @@ -0,0 +1,20 @@ +#ifndef GEOMETRY_SUBSET_H__ +#define GEOMETRY_SUBSET_H__ + +#include + +namespace LifeGraphics +{ + class GeometrySubset + { + public: + BOOL HasBones; + int Length; + int Offset; + int FaceCount; + int BonesCount; + std::vector MatrixList; + }; +} + +#endif \ No newline at end of file diff --git a/LifeGraphics/Globals.h b/LifeGraphics/Globals.h new file mode 100644 index 0000000..dc27622 --- /dev/null +++ b/LifeGraphics/Globals.h @@ -0,0 +1,53 @@ +#ifndef GLOBALS_H__ +#define GLOBALS_H__ + +#include "CG.h" +#include "ResourceManager.h" +#include "Types.h" + +#ifndef SAFE_DELETE +#define SAFE_DELETE(p) { if (p) { delete (p); (p)=NULL; } } +#endif +#ifndef SAFE_DELETE_ARRAY +#define SAFE_DELETE_ARRAY(p) { if (p) { delete[] (p); (p)=NULL; } } +#endif +#ifndef SAFE_RELEASE +#define SAFE_RELEASE(p) { if (p) { for(int lll=0; lll<100;lll++)(p)->Release(); (p)=NULL; } } +#endif + +enum ParticleBlendType +{ + ParticleBlendType_AlphaBased, + ParticleBlendType_ColorBased, + ParticleBlendType_Additive, + ParticleBlendType_None +}; + +enum EParticleFlags +{ + PART_FLAG_BILLBOARD = 0, // Usual particle + PART_FLAG_HORIZONTAL = 1, // Flat horisontal rounds on the water + PART_FLAG_UNDERWATER = 2, // Particle will be removed if go out from outdoor water + PART_FLAG_LINEPARTICLE = 4, // Draw billboarded line from vPosition to vPosition+vDirection + PART_FLAG_SWAP_XY = 8, // Alternative order of rotation (zxy) + PART_FLAG_SIZE_LINEAR = 16, // Change size liner with time + PART_FLAG_NO_OFFSET = 32, // Disable centering of static objects + PART_FLAG_DRAW_NEAR = 64, // Render particle in near space (weapon) + PART_FLAG_FOCUS_PLANE = 128, // Focus will spread partices along normal plane instead of direction axis. + PART_FLAG_NO_DRAW_UNDERWATER = 256, // Particle will be not visible when it position is under water + PART_FLAG_RIGIDBODY = 512, // If bRealPhysics set to true, physicalizing particles as rigid bodies + PART_FLAG_SPACELOOP = 1024, // Lock paticles in box around emitter position, use ParticleParams::vSpaceLoopBoxSize to set box size + PART_FLAG_SPACELIMIT = 2048, // Limit paticles by box around emitter position, use ParticleParams::vSpaceLoopBoxSize to set box size + PART_FLAG_SPEED_IN_GRAVITY_DIRECTION = 4096, // Ignores normal passed to particle effect and always uses gravity direction for initial speed direction. + PART_FLAG_BIND_POSITION_TO_EMITTER = 0x2000, // Always keep particle position binded to position of the emitter (for rockets etc...) + PART_FLAG_BIND_EMITTER_TO_CAMERA = 0x4000, // Attach emitter to camera pos + PART_FLAG_NO_INDOOR = 0x8000, // Kill particle if it enters indoor +}; + +enum QUALITY +{ + Q_HIGH = 0, + Q_MED = 1, + Q_LOW = 2 +}; +#endif \ No newline at end of file diff --git a/LifeGraphics/IGraphicsNode.h b/LifeGraphics/IGraphicsNode.h new file mode 100644 index 0000000..d8ea47a --- /dev/null +++ b/LifeGraphics/IGraphicsNode.h @@ -0,0 +1,102 @@ +#ifndef IGraphicsNode_h__ +#define IGraphicsNode_h__ + +#include "math/LifeMath.h" +#include +#include +#include "BoundingSphere.h" +#include +#include "LString.h" + +namespace LifeGraphics +{ + enum NodeType + { + NT_CAMERA, NT_LIGHT, NT_MESH, NT_PARTICLE_SYSTEM, NT_TERRAIN, NT_SCENE_ROOT + }; + + class IGraphicsNode + { + protected: + LifeCore::ITransform* defaultTransform; + LifeCore::ITransform* transform; + private: + + public: + long Id; + String Name; + std::vector childs; + IGraphicsNode* parent; + + bool castShadows; + LifeCore::BoundingBox3D bBox; + BoundingSphere bSphere; + LifeMath::float3 transformedBox[8]; + + const LifeCore::BoundingBox3D* getBoundingBox() + { + return &bBox; + } + const LifeMath::float3* getTransformedBox() + { + //if(!transform->IsValid()) + //{ + bBox.GetCorners(transformedBox); + LifeMath::float4x4 mTransform = transform->GetMatrix(); + for(int i = 0; i<8; i++) + { + transformedBox[i] = Vec3TransformCoordinate( mTransform, transformedBox[i]); + } + //} + return transformedBox; + } + const BoundingSphere* getBoundingSphere() + { + return &bSphere; + } + + LifeCore::ITransform* getTransform() + { + return transform; + } + void SetTransform(LifeCore::ITransform* _transform) + { + transform = _transform; + } + + void setParent(IGraphicsNode* node) + { + parent = node; + } + IGraphicsNode* getParent() + { + return parent; + } + IGraphicsNode() + :castShadows(true) + { + defaultTransform = new LifeCore::ITransform(); + transform = defaultTransform; + } + + virtual ~IGraphicsNode() + { + if(defaultTransform) + { + delete defaultTransform; + defaultTransform = NULL; + } + transform = NULL; + } + + void addChild(IGraphicsNode* node) + { + childs.push_back(node); + node->setParent(this); + } + virtual NodeType getType() = 0; + virtual void update(float timeElapsed) = 0; + }; +} + +#endif // ISceneNode_h__ \ No newline at end of file diff --git a/LifeGraphics/IMesh.h b/LifeGraphics/IMesh.h new file mode 100644 index 0000000..ba5855c --- /dev/null +++ b/LifeGraphics/IMesh.h @@ -0,0 +1,88 @@ +#ifndef IMesh_h__ +#define IMesh_h__ + +#include "IGraphicsNode.h" +#include "render/IVertexBuffer.h" +#include "render/IIndexBuffer.h" +//#include "render/IMaterial.h" +#include "render/IInputLayout.h" +#include "SkinInfo.h" + +#ifndef SAFE_DELETE +#define SAFE_DELETE(p) { if (p) { delete (p); (p)=NULL; } } +#endif +#ifndef SAFE_DELETE_ARRAY +#define SAFE_DELETE_ARRAY(p) { if (p) { delete[] (p); (p)=NULL; } } +#endif +#ifndef SAFE_RELEASE +#define SAFE_RELEASE(p) { if (p) { (p)->Release(); (p)=NULL; } } +#endif + +namespace LifeGraphics +{ + class GeometrySubset + { + public: + GeometrySubset() + { + MatrixList = (int*)0; + } + ~GeometrySubset() + { + //SAFE_DELETE_ARRAY(MatrixList); + } + int HasBones; + int Length; + int Offset, FaceCount, BonesCount; + int* MatrixList; + int matrixCount; + //IMaterial material; + //public LifeMaterial Material; + }; + + class IMesh + { + public: + int Id; + LifeCore::BoundingBox3D bBox; + LifeMath::float3 transformedBox[8]; + String Name; + IMesh(IVertexBuffer* _vertexData, IIndexBuffer* _indexData, GeometrySubset* _subsets, IInputLayout* _layout, int isSkinned, const LifeCore::BoundingBox3D& box, const String& name) + { + Name = name; + IsSkinned = isSkinned; + Id = 0; + vertexData = _vertexData; + indexData = _indexData; + subsets = _subsets; + inputLayout = _layout; + bBox = box; + + bBox.GetCorners(transformedBox); + + /* + if(subsetCount == 2) + subsetCount = 1;*/ + } + virtual ~IMesh() + { + SAFE_DELETE(vertexData); + SAFE_DELETE(indexData); + SAFE_DELETE_ARRAY(subsets); + SAFE_DELETE(inputLayout); + } + NodeType getType() + { + return NT_MESH; + } + int subsetCount; + public: + IVertexBuffer* vertexData; + IIndexBuffer* indexData; + GeometrySubset* subsets; + IInputLayout* inputLayout; + SkinInfo skin; + int IsSkinned; + }; +} +#endif // IMesh_h__ \ No newline at end of file diff --git a/LifeGraphics/IObject.h b/LifeGraphics/IObject.h new file mode 100644 index 0000000..52f2ec6 --- /dev/null +++ b/LifeGraphics/IObject.h @@ -0,0 +1,87 @@ +#ifndef IOBJECT_H__ +#define IOBJECT_H__ + +#include "LString.h" +#include "render/IMaterialProxy.h" +#include "Globals.h" +#include "math/LifeMath.h" +#include "IGraphicsNode.h" +#include "camera/Camera.h" + +namespace LifeGraphics +{ + struct PER_OBJECT_CONSTANTS + { + LifeMath::float4x4 mWorld; + }; + + class Submesh + { + public: + String name; + int offset; + int length; + }; + class IObject : public IGraphicsNode + { + protected: + IMaterialProxy** materials; + Submesh* subsets; + IConstantBuffer* perObjectCb; + IConstantBuffer* materialCb; + + public: + IObject() + { + perObjectCb = CG::me()->GetRenderer()->CreateConstantBuffer(sizeof(PER_OBJECT_CONSTANTS)); + } + virtual ~IObject(){} + virtual void bind() = 0; + virtual int getSumbeshCount() const = 0; + virtual void setMaterial(IMaterial* mat, int submesh, bool genShaders) = 0; + Submesh* getSubmesh(int id) + { + return subsets + id; + } + const IMaterialProxy* getMaterial(int submesh) const + { + return materials[submesh]; + } + IMaterialProxy* getMaterial(int submesh) + { + return materials[submesh]; + } + /*const IMaterial* getMaterial(String submesh) const + { + int count = getSumbeshCount(); + for(int i = 0; i < count; i++) + { + if(*getSubmeshName(i) == submesh) + return materials + i; + } + return NULL; + } + IMaterial* getMaterial(String submesh) + { + int count = getSumbeshCount(); + for(int i = 0; i < count; i++) + { + if(*getSubmeshName(i) == submesh) + return materials + i; + } + return NULL; + }*/ + + //virtual const String* getSubmeshName(int submesh) const = 0; + //virtual const LifeMath::float3intersect(const LifeMath::float3& rayStart, const LifeMath::float3& rayDir) const = 0; + //virtual const LifeMath::float3intersectSubmesh(const LifeMath::float3& rayStart, const LifeMath::float3& rayDir, int submesh) const = 0; + //virtual const LifeMath::float3intersectSubmesh(const LifeMath::float3& rayStart, const LifeMath::float3& rayDir, const String& submesh) const = 0; + virtual void selectLod(int lodNum) = 0; + virtual void selectLod(const Camera* cam) = 0; + virtual void render(int submesh) = 0; + //virtual int getVertexCount(int submesh) = 0; + //virtual int getFaceCount(int submesh) = 0; + }; +} + +#endif \ No newline at end of file diff --git a/LifeGraphics/IObjectMesh.h b/LifeGraphics/IObjectMesh.h new file mode 100644 index 0000000..6af631e --- /dev/null +++ b/LifeGraphics/IObjectMesh.h @@ -0,0 +1,184 @@ +#ifndef IObjectMesh_h__ +#define IObjectMesh_h__ + +#include "IObject.h" +#include "LString.h" +#include "Globals.h" +#include "render/IMaterial.h" +#include + +namespace LifeGraphics +{ +#define MAX_BONES_COUNT 128 + static const int PerObjectSkinningVSDataSize = 4 * 4 * 4 * MAX_BONES_COUNT; + + struct PerObjectSkinningVSData + { + LifeMath::float4x4* mWorld; + }; + + struct MeshLOD + { + IMesh* mesh; + float distance; + }; + class IObjectMesh : public IObject + { + private: + std::vector lods; + IMesh* mesh; + IConstantBuffer* skinBuffer; + LifeMath::float4x4* skinTempBuffer; + public: + IObjectMesh(IMesh* mesh) + { + setLod(mesh, 0.0f); + selectLod(0); + + materials = new IMaterialProxy*[mesh->subsetCount]; + memset(materials, 0, mesh->subsetCount * sizeof(materials[0])); + + //HACK + for(int i = 0; i < mesh->subsetCount; i++) + { + materials[i] = ResourceManager::me()->getDefaultMaterial()->CreateProxy(this); + } + + if(mesh->IsSkinned) + { + skinBuffer = CG::me()->GetRenderer()->CreateConstantBuffer(PerObjectSkinningVSDataSize); + skinTempBuffer = new LifeMath::float4x4[MAX_BONES_COUNT]; + + AnimationTrack* track = ResourceManager::me()->LoadAnimatrionTrack(AS_STRING("root/Data/Animations/miranda.sat")); + mesh->skin.controller.AddTrack(track); + } + } + IObjectMesh(String path) + { + IMesh* mesh = ResourceManager::me()->GetMesh(path); + setLod(mesh, 0.0f); + selectLod(0); + + materials = new IMaterialProxy*[mesh->subsetCount]; + memset(materials, 0, mesh->subsetCount * sizeof(materials[0])); + //HACK + for(int i = 0; i < mesh->subsetCount; i++) + { + materials[i] = ResourceManager::me()->getDefaultMaterial()->CreateProxy(this); + } + if(mesh->IsSkinned) + { + skinBuffer = CG::me()->GetRenderer()->CreateConstantBuffer(PerObjectSkinningVSDataSize); + skinTempBuffer = new LifeMath::float4x4[MAX_BONES_COUNT]; + + AnimationTrack* track = ResourceManager::me()->LoadAnimatrionTrack(AS_STRING("root/Data/Animations/miranda.sat")); + mesh->skin.controller.AddTrack(track); + } + } + ~IObjectMesh() + { + } + void setMaterial(IMaterial* mat, int submesh, bool genShaders) + { + materials[submesh] = mat->CreateProxy(this); + //NEED FIXUP SHADERS + /*EFFECT_FLAGS flags = EF_NONE; + if(mesh->IsSkinned) + { + flags = EF_SKINNING; + } + */ + // materials[submesh]->pixelShader = ResourceManager::me()->getShader(materials[submesh],flags, ST_PS); + // materials[submesh]->vertexShader = ResourceManager::me()->getShader(materials[submesh],flags, ST_VS); + + // materials[submesh]->vertexShaderLinearDepth = ResourceManager::me()->getShader(materials[submesh],flags | EF_LINEAR_DEPTH, ST_VS); + if(genShaders) + { + //materials[submesh]->g_buffer_pass_ps = ResourceManager::me()->getShader(materials[submesh],flags | EF_G_BUFFER_PREPASS, ST_PS); + //materials[submesh]->g_buffer_pass_vs = ResourceManager::me()->getShader(materials[submesh],flags | EF_G_BUFFER_PREPASS, ST_VS); + } + } + + void update(float dt) + { + PER_OBJECT_CONSTANTS objConst; + objConst.mWorld = getTransform()->GetMatrix(); + perObjectCb->Update(&objConst, sizeof(PER_OBJECT_CONSTANTS)); + + for(int i = 0; i< getSumbeshCount(); i++) + { + IMaterialProxy* mat = getMaterial(i); + /////////*if(mat->diffuseMap.ptr() && mat->diffuseMap->IsVideoTexture()) + ////////{ + //////// IVideoTexture* tex = static_cast(mat->diffuseMap.ptr()); + //////// tex->Update(dt); + ////////}*/ + } + + if (mesh->IsSkinned) + { + mesh->skin.controller.AdvanceTime(dt); + + mesh->skin.skeleton.GetMatrices(skinTempBuffer); + + skinBuffer->Update(skinTempBuffer, PerObjectSkinningVSDataSize); + } + } + void bind() + { + IRenderer* renderer = CG::me()->GetRenderer(); + renderer->SetVSConstantBuffer(perObjectCb, 0); + renderer->SetVertexBuffer(mesh->vertexData); + renderer->SetIndexBuffer(mesh->indexData); + + renderer->SetInputLayout(mesh->inputLayout); + renderer->SetPrimitiveTopology(PT_TRIANGLE_LIST); + + if(mesh->IsSkinned) + CG::me()->GetRenderer()->SetVSConstantBuffer(skinBuffer, 5); + } + void render(int submesh) + { + CG::me()->GetRenderer()->DrawIndexed(mesh->subsets[submesh].Offset,mesh->subsets[submesh].Length); + } + int getSumbeshCount() const + { + return mesh->subsetCount; + } + void selectLod(int lodNum) + { + mesh = lods[lodNum].mesh; + bBox = mesh->bBox; + } + void selectLod(const Camera* cam) + { + } + void setLod(IMesh* _mesh, float distance) + { + MeshLOD lod; + lod.distance = distance; + lod.mesh = _mesh; + + int lodCount = lods.size(); + if(!lodCount) + { + mesh = _mesh; + lods.push_back(lod); + Name = lods[0].mesh->Name; + return; + } + + for(std::vector::iterator iter = lods.begin(); iter!= lods.end(); ++iter) + { + if(iter->distance > distance) + lods.insert(iter,1,lod); + } + Name = lods[0].mesh->Name; + } + NodeType getType() + { + return NT_MESH; + } + }; +} +#endif // IObjectMesh_h__ \ No newline at end of file diff --git a/LifeGraphics/IRenderOutput.cpp b/LifeGraphics/IRenderOutput.cpp new file mode 100644 index 0000000..060c42c --- /dev/null +++ b/LifeGraphics/IRenderOutput.cpp @@ -0,0 +1,15 @@ +#include "IRenderOutput.h" + +namespace LifeGraphics +{ + IRenderOutput::IRenderOutput(LifeCore::IRenderWindow* out_window, ISwapChain* out_swapchain){ + window = out_window; + swap_chain = out_swapchain; + } + LifeCore::IRenderWindow* IRenderOutput::GetWindow(){ + return window; + } + ISwapChain* IRenderOutput::GetSwapChain(){ + return swap_chain; + } +} \ No newline at end of file diff --git a/LifeGraphics/IRenderOutput.h b/LifeGraphics/IRenderOutput.h new file mode 100644 index 0000000..4a57fba --- /dev/null +++ b/LifeGraphics/IRenderOutput.h @@ -0,0 +1,20 @@ +#ifndef IRenderOutput_h__ +#define IRenderOutput_h__ + +#include +#include "ISwapChain.h" + +namespace LifeGraphics +{ + class IRenderOutput + { + public: + IRenderOutput(LifeCore::IRenderWindow* out_window, ISwapChain* out_swapchain); + LifeCore::IRenderWindow* GetWindow(); + ISwapChain* GetSwapChain(); + private: + LifeCore::IRenderWindow* window; + ISwapChain* swap_chain; + }; +} +#endif // IRenderOutput_h__ diff --git a/LifeGraphics/IResource.h b/LifeGraphics/IResource.h new file mode 100644 index 0000000..57924bb --- /dev/null +++ b/LifeGraphics/IResource.h @@ -0,0 +1,37 @@ +#ifndef IRESOURCE_H__ +#define IRESOURCE_H__ + +//#include "IDataStream.h" +#include +namespace LifeGraphics +{ + class IResource + { + protected: + int _refCount; + public: + int CreationTime; + int MemoryUsed; + IResource() + :_refCount(0) + { + Grab(); + } + size_t Id; + + size_t GetID() + { + return Id; + } + + String Name; + void Grab(){_refCount++;} + void Release(){_refCount--;} + int RefCount(){return _refCount;} + virtual ~IResource(){} + // virtual void Load(Core::IDataStream* src) = 0; + // virtual void CompleteAsyncRequest(IResource* source) = 0; + }; +} + +#endif \ No newline at end of file diff --git a/LifeGraphics/IResourceCollection.h b/LifeGraphics/IResourceCollection.h new file mode 100644 index 0000000..ff0ef5f --- /dev/null +++ b/LifeGraphics/IResourceCollection.h @@ -0,0 +1,88 @@ +#ifndef IResourceCollection_h__ +#define IResourceCollection_h__ + +#include +#include +#include "resource_ptr.h" +#include "LString.h" +#include "Globals.h" + +namespace LifeGraphics +{ + template + class IResourceCollection + { + private: + typedef std::map> resourceMap; + int resCnt; + resourceMap collection; + + int lastId; + int NextId() + { + return lastId++; + } + + public: + int CollectGarbage() + { + int cnt = collection.size(); + + for(int i = 0; i < cnt; i++) + { + } + } + IResourceCollection() + { + resCnt = 0; + lastId = 0; + } + ~IResourceCollection() + { + } + int push_back(resource_ptr resource) + { + resource->Id = NextId(); + collection[resource->Id] = resource; + resCnt++; + return resource->Id; + } + void Release() + { + for(resourceMap::iterator it = collection.begin(); it != collection.end(); it++) + { + it->second->Release(); + } + } + int Exists(const String& name) + { + for(resourceMap::iterator it = collection.begin(); it != collection.end(); it++) + { + if(it->second->Name == name) + { + return 1; + } + } + return 0; + } + int GetResource(int id, resource_ptr& resource) + { + resource = collection[id]; + return 0; + } + int GetResource(String name, resource_ptr& resource) + { + for(resourceMap::iterator it = collection.begin(); it != collection.end(); it++) + { + if(it->second->Name == name) + { + resource = it->second; + return 1; + } + } + return 0; + } + }; +} + +#endif \ No newline at end of file diff --git a/LifeGraphics/IScene.h b/LifeGraphics/IScene.h new file mode 100644 index 0000000..18e2ee7 --- /dev/null +++ b/LifeGraphics/IScene.h @@ -0,0 +1,1203 @@ +#ifndef IScene_h__ +#define IScene_h__ + +#define _CRTDBG_MAP_ALLOC +#include +#include +#include "LifeDebug.h" + +#include "IGraphicsNode.h" +#include "LString.h" +#include +#include +//#include "IMesh.h" +#include "render/IConstantBuffer.h" +#include "math/LifeMath.h" +#include "Globals.h" +#include "camera/Camera.h" +#include "Light.h" +#include "RenderQueue.h" +#include "ResourceManager.h" +#include "Rectangle2D.h" +#include "ShadowMapManager.h" +#include "Types.h" +#include "RenderPass.h" +#include "render/PostEffect.h" +#include "render/PosteffectManager.h" +#include "render/PostHDR.h" +#include "render/PostDOF.h" +#include "render/PostSSAO.h" +#include "Terrain.h" +#include "Sky.h" +#include "vegetation/PlantManager.h" +#include "passPSSM.h" +#include "IObject.h" +#include "IObjectMesh.h" +#include "Timer.h" +#include "queries/IFrustumQuery.h" +#include "constants.h" +#include "renderpath/RenderPathOmniShadow.h" +#include "renderpath/RenderPathSpotShadow.h" +#include + +namespace LifeGraphics +{ + class TransformGraphRootNode : public IGraphicsNode + { + public: + TransformGraphRootNode(){} + ~TransformGraphRootNode(){} + NodeType getType() + { + return NT_SCENE_ROOT; + } + void update(float dt) + { + } + }; + + struct PER_SCENE_CONSTANTS + { + float fTime; + LifeMath::float3 ambient; + LifeMath::float2 screenSize; + LifeMath::float2 ssaoSize; + }; + + struct CullPattern + { + bool cullFrustum; + bool cullMaterial; + uint32 castShadows; + }; + + struct LPP_QUAD_VERT + { + LifeMath::float4 position; + LifeMath::float3 texIndex; + }; + + class IScene + { + public: + Camera* mainCam; + int INDEX; + void SetMainCamera(Camera* cam) + { + mainCam = cam; + } + + void ResizeOutput(int width, int height) + { + outputWidth = width; + outputHeight = height; + + delete sceneG_normal; + delete sceneG_color; + delete sceneG_depth; + delete sceneLbuf; + + //sceneRt = CG::me()->GetRenderer()->CreateRenderTarget(width, height, FORMAT_R16G16B16A16_FLOAT); + + sceneG_normal = CG::me()->GetRenderer()->CreateRenderTarget(width, height, FORMAT_R8G8B8A8_UNORM); + sceneG_color = CG::me()->GetRenderer()->CreateRenderTarget(width, height, FORMAT_R8G8B8A8_UNORM); + sceneG_depth = CG::me()->GetRenderer()->CreateRenderTarget(width, height, FORMAT_R16G16_FLOAT); + + sceneLbuf = CG::me()->GetRenderer()->CreateRenderTarget(width, height, FORMAT_R16G16B16A16_FLOAT); + //sceneDt = CG::me()->GetRenderer()->CreateDepthTarget(width, height, FORMAT_D24_UNORM_S8_UINT); + + mainCam->setAspect(float(width) / float(height)); + } + + IScene(int width, int height) + :sun(0), sceneTime(0.0f), sky(0), terra(0), plantManager(0) + { + pssm = new passPSSM(); + //pssm->Init(); + postManager = new PostEffectManager(); + + //sky = new Sky(); + //sky->Build(); + //terra = new Terrain(TD_X512,64,100); + terra = NULL; + + //plantManager = new PlantManager(); + //plantManager->Init(terra); + + mainCam = new Camera(); + INDEX = 0; + outputWidth = width; + outputHeight = height; + + SceneDescriptor desc; + desc.width = width; + desc.height = height; + postManager->Init(desc); + + /*dof = new PostDOF(); + dof->Init(desc); + postManager->Add(dof);*/ + + hdr = new PostHDR(); + hdr->Init(desc); + postManager->Add(hdr); + + angle = 0; + cbGlobal = CG::me()->GetRenderer()->CreateConstantBuffer(sizeof(PER_SCENE_CONSTANTS)); + + cbPerCamera = CG::me()->GetRenderer()->CreateConstantBuffer(sizeof(PER_CAMERA_CONSTANTS)); + cbPerCameraLight = CG::me()->GetRenderer()->CreateConstantBuffer(sizeof(PER_CAMERA_CONSTANTS)); + cbPointLights = CG::me()->GetRenderer()->CreateConstantBuffer(sizeof(POINT_LIGHT_DATA)); + cbSpotLights = CG::me()->GetRenderer()->CreateConstantBuffer(sizeof(SPOT_LIGHT_DATA)); + cbDirectionalLights = CG::me()->GetRenderer()->CreateConstantBuffer(sizeof(DIR_LIGHT_DATA)); + cbSkinData = CG::me()->GetRenderer()->CreateConstantBuffer(PerObjectSkinningVSDataSize); + + cbStencilSphere = CG::me()->GetRenderer()->CreateConstantBuffer(sizeof(LifeMath::float4)); + + cbShadowInfo = CG::me()->GetRenderer()->CreateConstantBuffer(sizeof(SHADOW_DATA)); + + lppBlend = CG::me()->GetRenderer()->CreateBlendState(true, false, BLEND_OP_ADD, BLEND_ONE, BLEND_ONE, 1 | 2 | 4 | 8); + gppBlend = CG::me()->GetRenderer()->CreateBlendState(false, false, BLEND_OP_ADD, BLEND_ONE, BLEND_ONE, 1 | 2 | 4 | 8); + + //disable color write + bsNoWrite = CG::me()->GetRenderer()->CreateBlendState(false, false, BLEND_OP_ADD, BLEND_ONE, BLEND_ONE, 0); + + appBlend = CG::me()->GetRenderer()->CreateBlendState(true, false, BLEND_OP_ADD, BLEND_SRC_ALPHA, BLEND_INV_SRC_ALPHA, 1 | 2 | 4 | 8); + + gppDepthState = CG::me()->GetRenderer()->CreateDSState(true, true, COMPARISON_LESS); + + lppDepthState = CG::me()->GetRenderer()->CreateDSState(false, false, COMPARISON_NEVER); + fppDepthState = CG::me()->GetRenderer()->CreateDSState(true, false, COMPARISON_EQUAL); + //always replace + //equal keep + + dsLitStencilOne = CG::me()->GetRenderer()->CreateDSState(true, false, COMPARISON_LESS_EQUAL, true, 0xff, COMPARISON_ALWAYS, + STENCIL_OP_KEEP, STENCIL_OP_INCR, STENCIL_OP_INCR, STENCIL_OP_KEEP); + + dsLitStencilTwo = CG::me()->GetRenderer()->CreateDSState(true, false, COMPARISON_GREATER_EQUAL, true, 0xff, COMPARISON_EQUAL, + STENCIL_OP_DECR, STENCIL_OP_KEEP, STENCIL_OP_KEEP, STENCIL_OP_KEEP); + + dsLitStencilLast = CG::me()->GetRenderer()->CreateDSState(false, false, COMPARISON_GREATER_EQUAL, true, 0xff, COMPARISON_EQUAL, STENCIL_OP_KEEP, STENCIL_OP_KEEP, STENCIL_OP_KEEP, STENCIL_OP_DECR_SAT); + + dsLitStencilLast2 = CG::me()->GetRenderer()->CreateDSState(false, false, COMPARISON_GREATER_EQUAL, true, 0xff, COMPARISON_EQUAL, STENCIL_OP_KEEP, STENCIL_OP_KEEP, STENCIL_OP_KEEP, STENCIL_OP_KEEP); + + //dsLitStencilLast = CG::me()->GetRenderer()->CreateDSState(true, false, COMPARISON_GREATER_EQUAL, true, 0xff, COMPARISON_EQUAL, STENCIL_OP_KEEP, STENCIL_OP_KEEP, STENCIL_OP_KEEP, STENCIL_OP_KEEP); + + PER_OBJECT_CONSTANTS constants; + constants.mWorld = LifeMath::float4x4(); + + rsCullBack = CG::me()->GetRenderer()->CreateRasterizerState(FILL_SOLID, CULL_BACK); + rsCullFront = CG::me()->GetRenderer()->CreateRasterizerState(FILL_SOLID, CULL_FRONT); + rsCullNone = CG::me()->GetRenderer()->CreateRasterizerState(FILL_SOLID, CULL_NONE); + + //spotLightTex = ResourceManager::me()->GetTexture(String(_text("root/Data/Textures/Spotlight.dds"))); + + rootNode = new TransformGraphRootNode(); + + //sceneRt = CG::me()->GetRenderer()->CreateRenderTarget(width, height, FORMAT_R16G16B16A16_FLOAT); + + sceneG_normal = CG::me()->GetRenderer()->CreateRenderTarget(width, height, FORMAT_R8G8B8A8_UNORM); + sceneG_color = CG::me()->GetRenderer()->CreateRenderTarget(width, height, FORMAT_R8G8B8A8_UNORM); + sceneG_depth = CG::me()->GetRenderer()->CreateRenderTarget(width, height, FORMAT_R16G16_FLOAT); + + sceneLbuf = CG::me()->GetRenderer()->CreateRenderTarget(width, height, FORMAT_R16G16B16A16_FLOAT); + //sceneDt = CG::me()->GetRenderer()->CreateDepthTarget(width, height, FORMAT_D24_UNORM_S8_UINT); + + lppPsSpot = ResourceManager::me()->CompileUserShaderPS(String(_text("Light_pre_pass_spot.psh"))); + lppPsSpotShadow = ResourceManager::me()->CompileUserShaderPS(String(_text("Light_pre_pass_spot_shadow.psh"))); + lppPsPoint = ResourceManager::me()->CompileUserShaderPS(String(_text("Light_pre_pass_point.psh"))); + lppPsPointShadow = ResourceManager::me()->CompileUserShaderPS(String(_text("Light_pre_pass_point_shadow.psh"))); + + lppPsDirectional = ResourceManager::me()->CompileUserShaderPS(String(_text("Light_pre_pass_directional.psh"))); + lppPsAmbient = ResourceManager::me()->CompileUserShaderPS(String(_text("Light_pre_pass_ambient.psh"))); + + lppVs = ResourceManager::me()->CompileUserShaderVS(String(_text("Light_pre_pass.vsh"))); + + lppPsStencilSphere = ResourceManager::me()->CompileUserShaderPS(String(_text("lighting/lpp_stencil_sphere.psh"))); + lppVsStencilSphere = ResourceManager::me()->CompileUserShaderVS(String(_text("lighting/lpp_stencil_sphere.vsh"))); + + lppVsStencilCone = ResourceManager::me()->CompileUserShaderVS(String(_text("lighting/lpp_stencil_cone.vsh"))); + + psComposeFinal = ResourceManager::me()->CompileUserShaderPS(STR("LPP_Compose.psh")); + + shadowManager = new ShadowMapManager(); + shadowManager->Init(CG::me()->GetRenderer()); + + rpOmniShadow = new RenderPath::RenderPathOmniShadow(shadowManager); + rpSpotShadow = new RenderPath::RenderPathSpotShadow(shadowManager); + + //fullscreen quad geometry + LPP_QUAD_VERT geometry[] = + { + LifeMath::float4(-1.0f,-1.0f,0.0f,1.0f), LifeMath::float3(0.0f,1.0f, 0.0f), + LifeMath::float4(-1.0f,1.0f,0.0f,1.0f), LifeMath::float3(0.0f,0.0f, 3.0f), + LifeMath::float4(1.0f,-1.0f,0.0f,1.0f), LifeMath::float3(1.0f,1.0f, 1.0f), + LifeMath::float4(1.0f,1.0f,0.0f,1.0f), LifeMath::float3(1.0f,0.0f, 2.0f), + }; + + lppQuadVb = CG::me()->GetRenderer()->CreateVertexBuffer(sizeof(LPP_QUAD_VERT) * 4, geometry, sizeof(LPP_QUAD_VERT)); + + IInputElement elements[] = + { + IInputElement("POSITION", 0, 0, FORMAT_R32G32B32A32_FLOAT, 0, 0, 0), + IInputElement("TEXCOORD", 0, 16, FORMAT_R32G32B32_FLOAT, 0, 0, 0), + }; + lppInputLayout = CG::me()->GetRenderer()->CreateInputLayout(elements, 2, lppVs); + + ISamplerDescription psDesc(Wrap, Wrap, Wrap, MinMagMipPoint); + ISamplerDescription pscDesc(Clamp, Clamp, Clamp, MinMagMipPoint); + ISamplerDescription lsDesc(Wrap, Wrap, Wrap, MinMagMipLinear); + + pointSampler = CG::me()->GetRenderer()->CreateTextureSampler(&psDesc); + linearSampler = CG::me()->GetRenderer()->CreateTextureSampler(&lsDesc); + + pointSamplerClamp = CG::me()->GetRenderer()->CreateTextureSampler(&pscDesc); + + ssao = new PostSSAO(this, Q_MED); + lightCullSphere = ResourceManager::me()->GetMesh("root/Data/Objects/Sphere_stencil3_opt.vmf"); + lightCullCone = ResourceManager::me()->GetMesh("root/Data/Objects/stencil_cone3_opt.vmf"); + // + } + ~IScene() + { + SAFE_DELETE(sceneG_depth); + SAFE_DELETE(sceneLbuf); + SAFE_DELETE(cbGlobal); + SAFE_DELETE(cbPerCamera); + SAFE_DELETE(cbPerCameraLight); + SAFE_DELETE(cbPointLights); + SAFE_DELETE(cbDirectionalLights); + SAFE_DELETE(cbSpotLights); + SAFE_DELETE(cbSkinData); + SAFE_DELETE(cbShadowInfo); + SAFE_DELETE(lppPsPoint); + SAFE_DELETE(lppPsSpot); + SAFE_DELETE(lppPsDirectional); + SAFE_DELETE(lppPsSpotShadow); + SAFE_DELETE(lppVs); + SAFE_DELETE(lppQuadVb); + SAFE_DELETE(lppInputLayout); + SAFE_DELETE(lppBlend); + SAFE_DELETE(gppBlend); + + SAFE_DELETE(lppDepthState); + SAFE_DELETE(gppDepthState); + SAFE_DELETE(fppDepthState); + SAFE_DELETE(shadowManager); + SAFE_DELETE(pointSampler); + SAFE_DELETE(linearSampler); + SAFE_DELETE(postManager); + SAFE_DELETE(hdr); + SAFE_DELETE(plantManager); + SAFE_DELETE(terra); + SAFE_DELETE(sky); + SAFE_DELETE(pssm); + + spotLightTex->Release(); + } + + LifeMath::Vec2i getOutputSize() + { + return LifeMath::Vec2i(outputWidth, outputHeight); + } + + String name; + void Update(float dt) + { + timeElapsed = dt; + sceneTime += dt; + + PER_SCENE_CONSTANTS globalConst; + globalConst.fTime = sceneTime; + globalConst.ambient = LifeMath::float3(0.05f,0.05f,0.05f); + globalConst.screenSize.X = (float)outputWidth; + globalConst.screenSize.Y = (float)outputHeight; + LifeMath::Vec2i sz = ssao->getSize(); + globalConst.ssaoSize.X = (float)(sz.X); + globalConst.ssaoSize.Y = (float)(sz.Y); + cbGlobal->Update(&globalConst, sizeof(PER_SCENE_CONSTANTS)); + + CG::me()->GetRenderer()->SetVSConstantBuffer(cbGlobal, 0); + CG::me()->GetRenderer()->SetPSConstantBuffer(cbGlobal, 3); + + int shadowedCount = 0; + for(std::vector::iterator it = nodeList.begin(); it != nodeList.end(); ++it) + { + IGraphicsNode* l = *it; + l->update(timeElapsed); + if(l->getType() == NT_LIGHT) + { + if(mainCam->getFrustum()->SphereInFrustum(l->getBoundingSphere())) + { + Light* fl = static_cast(l); + if(fl->lightType == LT_POINT) + visiblePointLights.push_back(fl); + else if(fl->lightType == LT_SPOT) + { + visibleSpotLights.push_back(fl); + } + } + } + else + { + if(mainCam->getFrustum()->BoxInFrustum(l->getTransformedBox())) + { + visibleRenderables.push_back(l); + IObject* obj = static_cast(l); + bool transparent = false; + for(int k = 0; k< obj->getSumbeshCount(); k++) + { + IMaterialProxy* mat = obj->getMaterial(k); + //TODO + /*if(mat->Opaque) + transparent = true;*/ + } + if(transparent) + { + visibleBlendRenderables.push_back(l); + } + } + } + } + } + + void Render(IRenderOutput* output) + { + renderer = CG::me()->GetRenderer(); + renderer->SetBlendState(gppBlend); + renderer->SetPSTextureSampler(linearSampler, 0); + renderer->SetPSTextureSampler(pointSampler, 1); + + //build g_buffer + int count = visibleRenderables.size(); + + PassDesc fpDesc; + fpDesc.camera = mainCam; + fpDesc.colorTarget = sceneG_depth; + fpDesc.depthTarget = output->GetSwapChain()->GetDepthBuffer(0); + fpDesc.frustum = mainCam->getFrustum(); + fpDesc.type = PT_COMPOSE_G_BUF; + fpDesc.objectList.reserve(count); + for(int i = 0; i < count; i++) + fpDesc.objectList.push_back(visibleRenderables[i]); + passList.push_back(fpDesc); + + //render shadows + /* PassDesc shadowDesc; + shadowDesc.type = PT_RENDER_SHADOW_MAP; + shadowDesc.objectList.reserve(visibleSpotLightsShadowed.size()); + for(UINT i = 0; i< visibleSpotLightsShadowed.size(); i++) + shadowDesc.objectList.push_back(visibleSpotLightsShadowed[i]); + passList.push_back(shadowDesc);*/ + + if(sun) + { + PassDesc shadowPSSMDesc; + shadowPSSMDesc.type = PT_RENDER_SHADOW_MAP_PSSM; + shadowPSSMDesc.objectList.push_back(sun); + shadowPSSMDesc.camera = mainCam; + passList.push_back(shadowPSSMDesc); + } + + //render lighting + PassDesc lightingDesc; + lightingDesc.camera = mainCam; + lightingDesc.colorTarget = sceneLbuf; + lightingDesc.depthTarget = output->GetSwapChain()->GetDepthBuffer(0); + lightingDesc.type = PT_LIGHTING; + passList.push_back(lightingDesc); + + //final rendering + PassDesc finalDesc; + finalDesc.camera = mainCam; + finalDesc.colorTarget = output->GetSwapChain()->GetBackBuffer(0); + finalDesc.depthTarget = output->GetSwapChain()->GetDepthBuffer(0); + finalDesc.frustum = mainCam->getFrustum(); + finalDesc.type = PT_COMPOSE_FINAL; + finalDesc.objectList.reserve(count); + for(int i = 0; i < count; i++) + finalDesc.objectList.push_back(visibleRenderables[i]); + passList.push_back(finalDesc); + + for(UINT i = 0; i< passList.size(); i++) + { + doPass(&passList[i]); + } + + + + visibleRenderables.clear(); + visibleRenderablesShadow.clear(); + visiblePointLights.clear(); + visibleSpotLights.clear(); + visibleBlendRenderables.clear(); + + // renderer->SetDepthStencilState(lppDepthState); + // ITexture2D* result = postManager->Execute(sceneRt, sceneDt, sceneGbuf, timeElapsed); + + //render scene to LDR back buffer + renderer->UnbindResourcesPS(); + // + + + //renderer->SetPSTexture(sceneRt, 0); + + /*if(false) + { + renderer->SetColorTarget(output->GetSwapChain()->GetBackBuffer(0)); + renderer->SetPSTexture(sceneLbuf, 0); + renderer->SetScissorRectangle(LifeMath::Rect2i(0, 0, outputWidth,outputHeight)); + renderer->SetBlendState(gppBlend); + renderer->SetDepthStencilState(lppDepthState); + renderer->SetViewport(LifeMath::Rect2i(0,0,outputWidth,outputHeight)); + renderer->SetPSTextureSampler(pointSampler, 0); + renderer->SetPSTextureSampler(pointSampler, 1); + renderer->RenderToTexture(); + } + + */ + + passList.clear(); + + + renderer->SetDepthStencilState(lppDepthState); + } + + void doPass(PassDesc* desc) + { + LifeMath::float4 color(0.0f,0.0f,0.0f,0.0f); + int count = desc->objectList.size(); + switch(desc->type) + { + case PT_COMPOSE_FINAL: + { + renderer->UnbindRenderTargets(); + renderer->UnbindResourcesPS(); + renderer->SetDepthStencilState(lppDepthState); + renderer->SetBlendState(gppBlend); + + renderer->SetPSConstantBuffer(cbGlobal, 0); + + renderer->SetPSTextureSampler(linearSampler, 0); + renderer->SetPSTextureSampler(pointSampler, 1); + + renderer->SetTargets(desc->colorTarget, desc->depthTarget); + + LifeMath::float4 clear_color(1,1,0,1); + renderer->ClearRenderTarget(desc->colorTarget, &clear_color); + + renderer->SetPixelShader(psComposeFinal); + renderer->SetPSTexture(sceneG_normal, 0); + renderer->SetPSTexture(ssao->getTexture(), 1); + renderer->SetPSTexture(sceneG_depth, 2); + renderer->SetPSTexture(sceneLbuf, 3); + renderer->SetPSTexture(sceneG_color, 4); + + LifeMath::Rect2i scr(0, 0, outputWidth, outputHeight); + renderer->SetScissorRectangle(scr); + renderer->SetViewport(scr); + + renderer->RenderToTextureNoPs(); + } + break; + case PT_COMPOSE_G_BUF: + { + if(terra) + terra->Update(desc->camera, sky->SunDirection); + if(plantManager) + plantManager->Update(timeElapsed, desc->camera); + + renderer->UnbindResourcesPS(); + renderer->UnbindRenderTargets(); + + const LifeMath::float3* corners = mainCam->getFrustum()->GetCorners(); + + LifeMath::Rect2i scr(0, 0, outputWidth, outputHeight); + renderer->SetScissorRectangle(scr); + renderer->SetViewport(scr); + + //collect camera info + PER_CAMERA_CONSTANTS camConst; + camConst.mView = desc->camera->getView(); + camConst.mProj = desc->camera->getProj(); + camConst.mInvVP = camConst.mView * camConst.mProj; + camConst.corner0 = corners[0]; + camConst.corner1 = corners[1]; + camConst.corner2 = corners[2]; + camConst.corner3 = corners[3]; + LifeMath::float3 camPos = desc->camera->getPosition(); + LifeMath::float3 camDir = desc->camera->getDirection(); + camConst.camPos = camPos; + camConst.camDir = camDir; + camConst.camNearFar.X = desc->camera->getNear(); + camConst.camNearFar.Y = desc->camera->getFar(); + camConst.camNearFar.Z = static_cast(outputWidth); + camConst.camNearFar.W = static_cast(outputHeight); + cbPerCamera->Update(&camConst, sizeof(PER_CAMERA_CONSTANTS)); + + renderer->SetDepthStencilState(gppDepthState); + renderer->SetVSConstantBuffer(cbPerCamera, 1); + renderer->SetPSConstantBuffer(cbPerCamera, 1); + + + + ITexture2D* tex[] = {sceneG_color, sceneG_normal, desc->colorTarget}; + // ITexture2D* tex2[] = {brdfTex, desc->colorTarget}; + renderer->SetTargets(tex, 3, desc->depthTarget); + /*renderer->SetTargets(tex2, 2,desc->depthTarget); + renderer->SetTargets(tex, 2,desc->depthTarget); + renderer->SetTargets(tex2, 2,desc->depthTarget);*/ + + LifeMath::float4 clearColor; + clearColor.X = 0.0f; + clearColor.Y = 0.0f; + clearColor.Z = 0.0f; + clearColor.W = 0.0f; + + LifeMath::float4 clearColor2(0,0,0,0); + + //clear g-buffer and brdf texture + renderer->ClearRenderTarget(desc->colorTarget, &clearColor); + renderer->ClearRenderTarget(sceneG_normal, &clearColor); + renderer->ClearRenderTarget(sceneG_color, &clearColor2); + + renderer->ClearDepthStencil(desc->depthTarget, 1.0f, 0); + + LifeMath::float4 col(0.2f,0.2f,0.2f,1); + renderer->ClearRenderTarget(sceneG_color, &col); + + + if(terra) + terra->Render(PT_COMPOSE_G_BUF); + + if(plantManager) + plantManager->Render(PT_COMPOSE_G_BUF); + + for(int i = 0; i < count; i++) + { + IObject* mesh = static_cast(desc->objectList[i]); + + mesh->bind(); + for(int i = 0; igetSumbeshCount(); i++) + { + IMaterialProxy* mat = mesh->getMaterial(i); + mat->SelectTechnique(MT_RENDER_DEFERRED); + mat->GetTechnique()->SelectPass(MP_G_FILL); + + IMaterialPass* pass = mat->GetTechnique()->GetPass(); + //renderer->SetPixelShader(pass->GetShader(ST_PS)); + //renderer->SetVertexShader(pass->GetShader(ST_VS)); + + IConstantBuffer* cb_mat = pass->GetConstantBuffer(); + if(cb_mat!= nullptr) renderer->SetPSConstantBuffer(cb_mat, 2); + pass->Apply(); + mesh->render(i); + } + } + } + + //SSAO + /* renderer->SetPSConstantBuffer(cbGlobal, 0); + renderer->SetVSConstantBuffer(cbGlobal, 0); + ssao->process(cbPerCamera, sceneGbuf, sceneDt);*/ + + /* renderer->UnbindRenderTargets(); + renderer->SetTargets(sceneSsao, sceneDt); + + renderer->SetPSTexture(sceneSsaoBlur, 0); + renderer->SetPSTexture(sceneGbuf, 9); + renderer->SetPixelShader(psSSAOBlur2); + renderer->Draw(4,0);*/ + + // printf("SSAO time: %f\n", timer.getTimeElapsed()); + + renderer->SetPrimitiveTopology(PT_TRIANGLE_LIST); + renderer->SetPSTextureSampler(linearSampler, 0); + renderer->SetPSTextureSampler(pointSampler, 1); + + break; + case PT_RENDER_SHADOW_MAP: + { + /*if(desc->objectList.size() > 0) + { + shadowManager->beginShadowRendering(desc->objectList.size()); + for(UINT i = 0; i< desc->objectList.size(); i++) + { + Light* l = static_cast(desc->objectList[i]); + shadowManager->beginLightRender(l); + + //collect light camera info + PER_CAMERA_CONSTANTS pccs = {0}; + pccs.camNearFar = LifeMath::float4(0.1f, l->getRange(), 0.0f,0.0f); + pccs.mView = l->getView(); + pccs.mProj = l->getProj(); + LifeMath::float3camPosl = l->getPosition(); + LifeMath::float3camDirl = l->getDirection(); + pccs.camPos = camPosl; + pccs.camDir = camDirl; + cbPerCameraLight->Update(&pccs, sizeof(PER_CAMERA_CONSTANTS)); + renderer->SetVSConstantBuffer(cbPerCameraLight, 1); + renderer->SetPSConstantBuffer(cbPerCameraLight, 1); + + //cull scene to take all visible to light geometry + for(std::vector::iterator it = nodeList.begin(); it != nodeList.end(); it++) + { + IGraphicsNode* obj = *it; + if(obj->getType() != NT_LIGHT && l->getFrustum()->SphereInFrustum(obj->getBoundingSphere())) + { + IObject* m = (IObject*)obj; + if(m->castShadows) + visibleRenderablesShadow.push_back(obj); + } + } + + //render objects to shadow map + int count = visibleRenderablesShadow.size(); + for(int i = 0; i < count; i++) + { + IObject* mesh = static_cast(visibleRenderablesShadow[i]); + mesh->bind(); + + for(int i = 0; i < mesh->getSumbeshCount(); i++) + { + IMaterial* mat = mesh->getMaterial(i); + if(!mat->AlphaBlendEnable) + { + renderer->SetVertexShader(mat->vertexShader); + + if(mat->diffuseMap.ptr()) + { + renderer->SetPSTexture(mat->diffuseMap.ptr(), 0); + } + if(mat->AlphaTest || mat->AlphaBlendEnable) + { + renderer->SetPixelShader(shadowManager->rtdPsAT); + } + else + { + renderer->SetPixelShader(shadowManager->rtdPs); + } + mesh->render(i); + } + } + } + + shadowManager->endLightRender(); + } + }*/ + } + break; + case PT_RENDER_SHADOW_MAP_PSSM: + { + renderer->SetPSTextureSampler(linearSampler, 0); + renderer->SetPSTextureSampler(pointSampler, 1); + + visibleRenderablesShadow.clear(); + //if(desc->objectList.size() > 0) + //{ + sun->setDirection(sky->SunDirection); + pssm->Begin(desc->camera, sun); + int splitCount = pssm->splitCount; + + for(int i = 0; iBeginSplit(i); + Frustum* f = &pssm->f; + //cull scene to take all visible to light geometry + for(std::vector::iterator it = nodeList.begin(); it != nodeList.end(); it++) + { + IGraphicsNode* obj = *it; + if(obj->getType() != NT_LIGHT && f->BoxInFrustum(obj->getTransformedBox())) + { + IMesh* m = (IMesh*)obj; + if(m->castShadows) + visibleRenderablesShadow.push_back(obj); + } + } + + PER_CAMERA_CONSTANTS pccs = {0}; + + pccs.camNearFar = LifeMath::float4(pssm->fNear, pssm->fFar, 0.0f,0.0f); + + pccs.mView = pssm->mLightView; + pccs.mProj = pssm->mLightProj; + pccs.mInvVP = pssm->mLightView * pssm->mLightProj; + + LifeMath::float3camPosl = pssm->vLightSource; + LifeMath::float3camDirl = pssm->vLightTarget; + + pccs.camPos = camPosl; + + pccs.camDir = camDirl; + + cbPerCameraLight->Update(&pccs, sizeof(PER_CAMERA_CONSTANTS)); + renderer->SetVSConstantBuffer(cbPerCameraLight, 1); + renderer->SetPSConstantBuffer(cbPerCameraLight, 1); + + renderer->SetVSConstantBuffer(cbGlobal, 0); + plantManager->Render(PT_RENDER_SHADOW_MAP_PSSM); + + //render objects to shadow map + int count = visibleRenderablesShadow.size(); + for(int i = 0; i < count; i++) + { + IMesh* mesh = static_cast(visibleRenderablesShadow[i]); + PER_OBJECT_CONSTANTS objConst; + objConst.mWorld = mesh->getTransform()->GetMatrix(); + cbPerObject->Update(&objConst, sizeof(PER_OBJECT_CONSTANTS)); + + if (mesh->IsSkinned) + { + PerObjectSkinningVSData skinData; + skinData.mWorld = new LifeMath::float4x4[MAX_BONES_COUNT]; + + mesh->skin.skeleton.GetMatrices(skinData.mWorld); + + cbSkinData->Update(skinData.mWorld, PerObjectSkinningVSDataSize); + + renderer->SetVSConstantBuffer(cbSkinData, 5); + delete[] skinData.mWorld; + } + + renderer->SetVSConstantBuffer(cbPerObject, 0); + /*renderer->SetVertexBuffer(mesh->vertexData); + renderer->SetIndexBuffer(mesh->indexData); + + renderer->SetInputLayout(mesh->inputLayout); + renderer->SetPrimitiveTopology(PT_TRIANGLE_LIST); + for(int j = 0; j < mesh->subsetCount; j++) + { + IMaterial* mat = &mesh->subsets[j].material; + //renderer->SetPixelShader(mat->g_buffer_pass_ps); + renderer->SetVertexShader(mat->vertexShader); + + if(mat->diffuseMap.ptr()) + { + renderer->SetPSTexture(mat->diffuseMap.ptr(), 0); + //renderer->SetPSTexture(mat->normalMap.ptr(), 2); + } + renderer->DrawIndexed(mesh->subsets[j].Offset,mesh->subsets[j].Length); + } + } + + pssm->EndSplit();*/ + } + pssm->End(); + renderer->SetVSConstantBuffer(cbGlobal, 0); //set back global cb //FIX IT: per object constant buffer should not be set to slot 0 !! + } + break; + case PT_LIGHTING: + { + renderer->UnbindResourcesPS(); + renderer->UnbindRenderTargets(); + + renderer->SetPSTexture(sceneG_depth, 0); + renderer->SetPSTexture(sceneG_color, 1); + renderer->SetPSTexture(sceneG_normal, 9); + + renderer->SetTargets(desc->colorTarget, desc->depthTarget); + //-----begin lighting + renderer->SetVSConstantBuffer(cbPerCamera, 1); + renderer->SetPSConstantBuffer(cbPerCamera, 1); + renderer->SetViewport(LifeMath::Rect2i(0, 0, outputWidth, outputHeight)); + + renderer->SetDepthStencilState(lppDepthState); + + LifeMath::float4 clear_color(0,0,0,0); + renderer->ClearRenderTarget(desc->colorTarget, &clear_color); + + renderer->SetInputLayout(lppInputLayout); + renderer->SetBlendState(lppBlend); + renderer->SetVertexBuffer(lppQuadVb); + renderer->SetPrimitiveTopology(PT_TRIANGLE_STRIP); + renderer->SetVertexShader(lppVs); + + //render sun + if(sun) + { + pssm->UpdateBuffer(); + //renderer->SetPSTexture(pssm->depth, 1); //NEED TO SELECT DiFFERENT TEXTURE SLOT! 1 IS FOR DIFFuSE NOW + renderer->SetPSConstantBuffer(pssm->cbPssm, 3); + + renderer->SetPixelShader(lppPsDirectional); + renderer->SetPSConstantBuffer(cbDirectionalLights, 2); + DIR_LIGHT_DATA dlightData; + LifeMath::float4x4 invVP2 = desc->camera->getView() * desc->camera->getProj(); + + Light* l = sun; + LifeMath::float3 dir = sky->SunDirection; + LifeMath::Vec3Normalize(&dir); + + dlightData.Direction = dir; + dlightData.Intensity = l->intensity; + + dlightData.Color.X = l->color.X; + dlightData.Color.Y = l->color.Y; + dlightData.Color.Z = l->color.Z; + + dlightData.SpecularPow = 30.0f; + cbDirectionalLights->Update(&dlightData, sizeof(DIR_LIGHT_DATA)); + // + renderer->Draw(4,0); + } + + //-----render point lights + int countL = visiblePointLights.size(); + if(countL) + { + renderer->SetDepthStencilState(lppDepthState); + + renderer->SetTargets(desc->colorTarget, desc->depthTarget); + renderer->SetVSConstantBuffer(cbPerCamera, 1); + renderer->SetPSConstantBuffer(cbPerCamera, 1); + + renderer->SetPSConstantBuffer(cbPointLights, 2); + renderer->SetVSConstantBuffer(cbPointLights, 2); + renderer->SetPixelShader(lppPsPoint); + renderer->SetVertexShader(lppVsStencilSphere); + + renderer->SetPSTexture(sceneG_depth, 0); + renderer->SetPSTexture(sceneG_color, 1); + renderer->SetPSTexture(sceneG_normal, 9); + + //sceneG_color->ToFile("C:\\color.dds"); + //bind sphere mesh + renderer->SetIndexBuffer(lightCullSphere->indexData); + renderer->SetVertexBuffer(lightCullSphere->vertexData); + renderer->SetPrimitiveTopology(PT_TRIANGLE_LIST); + renderer->SetInputLayout(lightCullSphere->inputLayout); + + POINT_LIGHT_DATA lightData; + LifeMath::float4x4 invVP2 = desc->camera->getView() * desc->camera->getProj(); + + renderer->ClearDepthStencil(desc->depthTarget, 2, 1, 0); + + for(int i = 0; i < countL; i++) + { + Light* l = visiblePointLights[i]; + + if(l->castShadows) + { + renderer->SetDepthStencilState(gppDepthState); + renderer->SetBlendState(gppBlend); + rpOmniShadow->SetLight(*visiblePointLights[i]); + rpOmniShadow->Render(this); + + renderer->SetViewport(LifeMath::Rect2i(0, 0, outputWidth, outputHeight)); + + renderer->SetTargets(desc->colorTarget, desc->depthTarget); + renderer->SetVSConstantBuffer(cbPerCamera, 1); + renderer->SetPSConstantBuffer(cbPerCamera, 1); + + renderer->SetPSConstantBuffer(cbPointLights, 2); + renderer->SetVSConstantBuffer(cbPointLights, 2); + + renderer->SetPSTexture(sceneG_depth, 0); + renderer->SetPSTexture(sceneG_color, 1); + renderer->SetPSTexture(sceneG_normal, 9); + renderer->SetPSTextureCube(rpOmniShadow->GetShadowMap(), 8); + + renderer->SetPixelShader(lppPsPointShadow); + renderer->SetVertexShader(lppVsStencilSphere); + + renderer->SetIndexBuffer(lightCullSphere->indexData); + renderer->SetVertexBuffer(lightCullSphere->vertexData); + renderer->SetPrimitiveTopology(PT_TRIANGLE_LIST); + renderer->SetInputLayout(lightCullSphere->inputLayout); + } + else + { + renderer->SetPixelShader(lppPsPoint); + } + //setup light constants + LifeMath::float3 pos = l->getPosition(); + lightData.Position = pos; + lightData.Intensity = l->intensity; + lightData.Color.X = l->color.X; + lightData.Color.Y = l->color.Y; + lightData.Color.Z = l->color.Z; + lightData.Range = l->getRange(); + cbPointLights->Update(&lightData, sizeof(POINT_LIGHT_DATA)); + + renderer->SetRasterizerState(rsCullBack); + renderer->SetDepthStencilState(dsLitStencilOne, 0); + renderer->SetBlendState(bsNoWrite); + renderer->DrawIndexed(0, lightCullSphere->subsets[0].Length, 0); + + renderer->SetRasterizerState(rsCullFront); + renderer->SetDepthStencilState(dsLitStencilTwo, 0); + renderer->SetBlendState(lppBlend); + renderer->DrawIndexed(0, lightCullSphere->subsets[0].Length, 0); + } + } + + renderer->SetRasterizerState(rsCullBack); + + //render spot lights + int spotCount = visibleSpotLights.size(); + if(spotCount) + { + renderer->UnbindResourcesPS(); + renderer->SetPSConstantBuffer(cbSpotLights, 3); + renderer->SetVertexShader(lppVs); + SPOT_LIGHT_DATA lightDataSpot; + renderer->SetPSTexture(sceneG_depth, 0); + renderer->SetPSTexture(sceneG_color, 1); + renderer->SetPSTexture(sceneG_normal, 9); + for(int i = 0; i < spotCount; i++) + { + Light* l = visibleSpotLights[i]; + renderer->SetPSTextureSampler(linearSampler, 0); + renderer->SetPSTextureSampler(pointSampler, 1); + + if(l->castShadows) + { + renderer->SetBlendState(gppBlend); + rpSpotShadow->SetLight(*l); + rpSpotShadow->Render(this); + + renderer->SetViewport(LifeMath::Rect2i(0, 0, outputWidth, outputHeight)); + renderer->SetTargets(desc->colorTarget, desc->depthTarget); + + renderer->SetVSConstantBuffer(cbPerCamera, 1); + renderer->SetPSConstantBuffer(cbPerCamera, 1); + + renderer->SetPSTexture(rpSpotShadow->GetShadowMap(), 6); + + renderer->SetPSTextureSampler(linearSampler, 0); + renderer->SetPSTextureSampler(pointSampler, 1); + + renderer->SetVertexShader(lppVs); + renderer->SetPixelShader(lppPsSpotShadow); + + renderer->SetPSTexture(sceneG_depth, 0); + renderer->SetPSTexture(sceneG_color, 1); + renderer->SetPSTexture(sceneG_normal, 9); + } + else + { + renderer->SetPixelShader(lppPsSpot); + } + + renderer->SetPSTexture(spotLightTex.ptr(), 4); + + SHADOW_DATA shadowInfo = shadowManager->getShadowInfo(i); + cbShadowInfo->Update(&shadowInfo, sizeof(SHADOW_DATA)); + + LifeMath::float3 pos = l->getPosition(); + + LifeMath::Rect2i rect(0, 0, outputWidth, outputHeight); + + renderer->SetScissorRectangle(rect); + + lightDataSpot.Position = pos; + lightDataSpot.Direction = l->getDirection() - l->getPosition(); + LifeMath::Vec3Normalize(&lightDataSpot.Direction); + lightDataSpot.Intensity = l->intensity; + lightDataSpot.Color = l->color; + lightDataSpot.Range = l->getRange(); + lightDataSpot.InnerAngle = cosf(l->getInnerAngle() * 0.5f); + lightDataSpot.OuterAngle = cosf(l->getOuterAngle() * 0.5f); + lightDataSpot.lightMatrix = l->getMatrix(); + lightDataSpot.lightView = l->getView(); + lightDataSpot.meshMatrix = l->getMeshMatrix(); + + lightDataSpot.ZNear = 0.1f; + //Mat4x4Identity(&lightDataSpot.meshMatrix); + cbSpotLights->Update(&lightDataSpot, sizeof(SPOT_LIGHT_DATA)); + + renderer->SetVertexShader(lppVsStencilCone); + + //setup cone geometry + renderer->SetVertexBuffer(lightCullCone->vertexData); + renderer->SetIndexBuffer(lightCullCone->indexData); + renderer->SetInputLayout(lightCullCone->inputLayout); + renderer->SetPrimitiveTopology(PT_TRIANGLE_LIST); + + renderer->SetPSConstantBuffer(cbSpotLights, 3); + renderer->SetVSConstantBuffer(cbSpotLights, 3); + + renderer->SetRasterizerState(rsCullBack); + renderer->SetDepthStencilState(dsLitStencilOne, 0); + renderer->SetBlendState(bsNoWrite); + renderer->DrawIndexed(0, lightCullCone->subsets[0].Length, 0); + + renderer->SetRasterizerState(rsCullFront); + renderer->SetDepthStencilState(dsLitStencilTwo, 0); + renderer->SetBlendState(lppBlend); + renderer->DrawIndexed(0, lightCullCone->subsets[0].Length, 0); + } + } + + //end lighting + LifeMath::Rect2i rect2(0,0,outputWidth, outputHeight); + renderer->SetScissorRectangle(rect2); + renderer->SetBlendState(gppBlend); + renderer->SetDepthStencilState(fppDepthState); + + renderer->UnbindResourcesPS(); + renderer->UnbindRenderTargets(); + } + break; + } + desc->objectList.clear(); + } + + void PushNode(IGraphicsNode* node) + { + //if(node->Id >=0) + //{ + if(node->getType() == NT_LIGHT) + { + Light* l = static_cast(node); + if(l->lightType == LT_DIRECTIONAL) + sun = l; + else + nodeList.push_back(node); + rootNode->addChild(node); + } + else + { + nodeList.push_back(node); + rootNode->addChild(node); + } + + //} + //else + //{ + // return; + //} + } + + void PerformQuery(IFrustumQuery* query) + { + for(std::vector::iterator it = nodeList.begin(); it != nodeList.end(); ++it) + { + IGraphicsNode* l = *it; + + if(!query->Filration || (((query->filter & l->getType()) !=0))) + { + if(l->getType() == NT_LIGHT) + { + if(query->GetFrustum().SphereInFrustum(l->getBoundingSphere())) + { + query->AddNode(l); + } + } + else + { + if(query->GetFrustum().BoxInFrustum(l->getTransformedBox())) + { + query->AddNode(l); + } + } + } + } + } + private: + IMesh* lightCullSphere; + IMesh* lightCullCone; + + IRenderer* renderer; + float angle; + LifeCore::BoundingBox3D lbb; + LifeMath::float3 lbbCorners[8]; + //ITexture2D* sceneRt; + ITexture2D* sceneLbuf; + //ITexture2D* sceneDt; + + ITexture2D* sceneG_depth; + ITexture2D* sceneG_color; + ITexture2D* sceneG_normal; + + texture_ptr ssaoRandom; + + std::vector nodeList; + IGraphicsNode* rootNode; + IConstantBuffer* cbGlobal; + IConstantBuffer* cbPerCamera; + IConstantBuffer* cbPerCameraLight; + IConstantBuffer* cbPointLights; + IConstantBuffer* cbDirectionalLights; + IConstantBuffer* cbSpotLights; + IConstantBuffer* cbSkinData; + + IConstantBuffer* cbStencilSphere; + + IConstantBuffer* cbShadowInfo; + + RenderQueue renderableQueue; + std::vector visibleRenderables; + std::vector visibleRenderablesShadow; + std::vector visiblePointLights; + std::vector visibleSpotLights; + std::vector visibleBlendRenderables; + Light* sun; + IShader* lppPsPoint; + IShader* lppPsPointShadow; + IShader* lppPsSpot; + IShader* lppPsDirectional; + IShader* lppPsSpotShadow; + IShader* lppPsAmbient; + IShader* lppPsStencilSphere; + IShader* lppVsStencilSphere; + IShader* lppVsStencilCone; + IShader* lppVs; + IShader* psComposeFinal; + IShader* psDownSampleDepth; + IVertexBuffer* lppQuadVb; + IInputLayout* lppInputLayout; + IBlendState* lppBlend; + IBlendState* gppBlend; + + IBlendState* appBlend; + IBlendState* bsNoWrite; + + IDepthStencilState* lppDepthState; + IDepthStencilState* gppDepthState; + IDepthStencilState* fppDepthState; + IDepthStencilState* dsLitStencilOne; + IDepthStencilState* dsLitStencilTwo; + IDepthStencilState* dsLitStencilLast; + IDepthStencilState* dsLitStencilLast2; + + ShadowMapManager* shadowManager; + texture_ptr spotLightTex; + + std::vector passList; + + ITextureSampler* pointSampler; + ITextureSampler* linearSampler; + + ITextureSampler* pointSamplerClamp; + + IRasterizerState* rsCullBack; + IRasterizerState* rsCullFront; + IRasterizerState* rsCullNone; + + int outputWidth; + int outputHeight; + + PostEffectManager* postManager; + + PostHDR* hdr; + PostDOF* dof; + PostSSAO* ssao; + PlantManager* plantManager; + + Terrain* terra; + Sky* sky; + float timeElapsed; + + passPSSM* pssm; + + float sceneTime; + + LifeCore::Timer timer; + IObjectMesh* tm; + RenderPath::RenderPathOmniShadow* rpOmniShadow; + RenderPath::RenderPathSpotShadow* rpSpotShadow; + }; +} + +#endif // IScene_h__ \ No newline at end of file diff --git a/LifeGraphics/ISkinnedMesh.h b/LifeGraphics/ISkinnedMesh.h new file mode 100644 index 0000000..41c5d6c --- /dev/null +++ b/LifeGraphics/ISkinnedMesh.h @@ -0,0 +1,11 @@ +#ifndef ISkinnedMesh_h__ +#define ISkinnedMesh_h__ + +#include "IMesh.h" +namespace LifeGraphics +{ + class ISkinnedMesh : public IMesh + { + }; +} +#endif // ISkinnedMesh_h__ \ No newline at end of file diff --git a/LifeGraphics/ISwapChain.h b/LifeGraphics/ISwapChain.h new file mode 100644 index 0000000..aa14ca8 --- /dev/null +++ b/LifeGraphics/ISwapChain.h @@ -0,0 +1,23 @@ +#ifndef ISwapChain_h__ +#define ISwapChain_h__ + +#include "render/ITexture2D.h" +#include "Types.h" +#include + +namespace LifeGraphics +{ + class ISwapChain{ + public: + EVENT1(SwapChainEvent, ISwapChain*); + SwapChainEvent Event_Resized; + SwapChainEvent Event_Presented; + + virtual LifeGraphics::ITexture2D* GetBackBuffer(uint8 id) = 0; + virtual LifeGraphics::ITexture2D* GetDepthBuffer(uint8 id) = 0; + virtual void Resize(uint16 width, uint16 height) = 0; + virtual void Present() = 0; + virtual ~ISwapChain(){} + }; +} +#endif // ISwapChain_h__ diff --git a/LifeGraphics/LifeDebug.h b/LifeGraphics/LifeDebug.h new file mode 100644 index 0000000..11e4276 --- /dev/null +++ b/LifeGraphics/LifeDebug.h @@ -0,0 +1,24 @@ +#ifndef LifeDebug_h__ +#define LifeDebug_h__ + +//#define _CRTDBG_MAP_ALLOC +//#include +/* +#ifdef _CRTDBG_MAP_ALLOC + +#define malloc(s) _malloc_dbg(s,_NORMAL_BLOCK,__FILE__,__LINE__) +#define calloc(c,s) _calloc_dbg(c,s,_NORMAL_BLOCK,__FILE__,__LINE__) +#define realloc(p,s) _realloc_dbg(p,s,_NORMAL_BLOCK,__FILE__,__LINE__) +#define _expand(p,s) _expand_dbg(p,s,_NORMAL_BLOCK,__FILE__,__LINE__) +#define free(p) _free_dbg(p,_NORMAL_BLOCK) +#define _msize(p) _msize_dbg(p,_NORMAL_BLOCK) + +#endif + +#ifdef _DEBUG +#ifdef _CRTDBG_MAP_ALLOC +#define new new(_NORMAL_BLOCK, __FILE__, __LINE__) +#endif +#endif */ + +#endif // LifeDebug_h__ \ No newline at end of file diff --git a/LifeGraphics/LifeEngine.config b/LifeGraphics/LifeEngine.config new file mode 100644 index 0000000..8cec188 --- /dev/null +++ b/LifeGraphics/LifeEngine.config @@ -0,0 +1 @@ +// ADD PREDEFINED MACROS HERE! diff --git a/LifeGraphics/LifeEngine.cpp b/LifeGraphics/LifeEngine.cpp new file mode 100644 index 0000000..5ee97d5 --- /dev/null +++ b/LifeGraphics/LifeEngine.cpp @@ -0,0 +1,85 @@ +#include "LifeEngine.h" +#define COMPILE_WITH_DX11 +//#define COMPILE_WITH_SW +#include "Globals.h" +#include "IScene.h" +#ifdef COMPILE_WITH_DX11 +#include "render/dx11/RendererDX11.h" +#endif + +#ifdef COMPILE_WITH_SW +#include "RendererSoftware.h" +#endif + +namespace LifeGraphics +{ + LifeEngine::LifeEngine() + :engine_time(0.01f), appTime(0.0f) + { + } + + LifeEngine::~LifeEngine() + { + g_pRenderer->BeginCleanup(); + ResourceManager::me()->ReleaseResources(); + g_pRenderer->EndCleanup(); + SAFE_DELETE(g_pRenderer); + } + + PluginServer* LifeEngine::GetPluginServer() + { + return &g_pluginServer; + } + + IRenderer* LifeEngine::Init(LifeGraphics::RENDERER_TYPE type, RenderParams params) + { + switch(type) + { + case RT_DIRECTX_9: + //Nothing for now + break; + case RT_DIRECTX_10: + /*g_pluginServer.LoadPlugin("RendererDX10.dll"); + g_pRenderer = g_pluginServer.GetRenderer(0); + printf(g_pRenderer->GetRendererName().c_str()); + g_pRenderer->Open(params);*/ + break; + case RT_DIRECTX_11: + //g_pluginServer.LoadPlugin("RendererDX11.dll"); + g_pRenderer = new RendererDX11(); + g_pRenderer->Open(params); + break; + } + + //window = params.Window; + //window->Event_Resize += NewDelegate(this, &Video::LifeEngine::onWindowResize); + + CG::me()->SetRenderer(g_pRenderer); + + + + debugDrawer = new IDebugDrawer(g_pRenderer); + return g_pRenderer; + } + + IRenderer* LifeEngine::GetRenderer() + { + return g_pRenderer; + } + + void LifeEngine::onWindowResize(LifeCore::IRenderWindow* window) + { + printf("LOOOOOL!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n WINDOW RESIZED!!!!!!!!!!!!!!!!!!!!!\n!!!!!!!!!!!!!!!!!!!!!!\n"); + } + + void LifeEngine::RenderScene(LifeGraphics::IScene* scene, IRenderOutput* output) + { + scene->Render(output); + } + IRenderOutput* LifeEngine::CreateRenderOutput(LifeCore::IRenderWindow* wnd){ + LifeGraphics::ISwapChain* sc = GetRenderer()->CreateSwapChain(wnd); + if(sc == nullptr) + return nullptr; + return new IRenderOutput(wnd, sc); + } +} \ No newline at end of file diff --git a/LifeGraphics/LifeEngine.h b/LifeGraphics/LifeEngine.h new file mode 100644 index 0000000..3190c6b --- /dev/null +++ b/LifeGraphics/LifeEngine.h @@ -0,0 +1,56 @@ +#ifndef LifeEngine_h__ +#define LifeEngine_h__ + +#include "render/IRenderer.h" +//#include "RendererDX10.h" +#include "render/RenderParams.h" +#include "PluginServer.h" +#include "camera/Camera.h" +#include "Timer.h" +#include "LString.h" +#include "IScene.h" +#include "debug/IDebugDrawer.h" +#include "RenderWindow.h" +#include "IRenderOutput.h" + +namespace LifeGraphics +{ + class LIFE_EXPORTS LifeEngine + { + public: + float appTime; + IDebugDrawer* debugDrawer; + float engine_time; + LifeEngine(); + ~LifeEngine(); + IRenderer* Init(LifeGraphics::RENDERER_TYPE type, RenderParams params); + IRenderer* GetRenderer(); + PluginServer* GetPluginServer(); + void RenderScene(LifeGraphics::IScene* scene, IRenderOutput* output); + ResourceManager* GetResourceManager() + { + return ResourceManager::me(); + } + float FrameEnd() + { + systemTimer.Stop(); + engine_time = systemTimer.getTimeElapsed(); + appTime += engine_time; + systemTimer.Start(); + ResourceManager::me()->CollectGarbage(engine_time); + return engine_time; + } + float GetTimeElapsed() + { + return engine_time; + } + IRenderOutput* CreateRenderOutput(LifeCore::IRenderWindow* wnd); + private: + void onWindowResize(LifeCore::IRenderWindow* window); + IRenderer* g_pRenderer; + PluginServer g_pluginServer; + LifeCore::Timer systemTimer; + }; +} + +#endif // LifeEngine_h__ \ No newline at end of file diff --git a/LifeGraphics/LifeGraphics.vcxproj b/LifeGraphics/LifeGraphics.vcxproj new file mode 100644 index 0000000..dbaa270 --- /dev/null +++ b/LifeGraphics/LifeGraphics.vcxproj @@ -0,0 +1,561 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {54BC1734-E9F0-4170-A267-91E84F3EACB5} + LifeEngine + Win32Proj + <Project Location In Database> + <Source Control Database> + <Local Binding Root of Project> + Mercurial Source Control Package + $(VCTargetsPath11) + LifeGraphics + + + + StaticLibrary + MultiByte + true + v120 + + + StaticLibrary + MultiByte + true + v120 + + + StaticLibrary + MultiByte + v120 + + + StaticLibrary + MultiByte + v120 + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(SolutionDir)$(ProjectName)\lib\ + $(SolutionDir)$(ProjectName)\lib\ + $(SolutionDir)$(ProjectName)\tmp\$(Configuration)\ + $(Configuration)\$(ProjectName) + $(SolutionDir)$(ProjectName)\lib\ + lib\ + $(SolutionDir)$(ProjectName)\tmp\$(Configuration)\ + $(Configuration)\ + $(ProjectName)_d + $(ProjectName)_d + + + + Disabled + $(REPO_DIR)\zlib-1.2.5;$(DXSDK_DIR)\Include;$(SolutionDir)/LifeCore/;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;LIFEENGINE_EXPORTS;%(PreprocessorDefinitions) + false + EnableFastChecks + MultiThreadedDebugDLL + false + + + Level3 + false + EditAndContinue + true + $(IntDir) + + + true + + + $(OutDir)$(TargetName)$(TargetExt) + $(SolutionDir)\LifeCore\lib;$(REPO_DIR)\zlib-1.2.5\lib;$(DXSDK_DIR)\Lib\x86;%(AdditionalLibraryDirectories) + LifeCore_d.lib;d3dx11.lib;DXGI.lib;d3d11.lib;d3dcompiler.lib + + + + + Disabled + $(REPO_DIR)\zlib-1.2.5;$(REPO_DIR)\freetype-2.4.3\include;$(DXSDK_DIR)\Include;.\include;$(SolutionDir)\LifeCore\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;LIFEENGINE_EXPORTS;%(PreprocessorDefinitions) + false + EnableFastChecks + MultiThreadedDebugDLL + false + + + Level3 + false + ProgramDatabase + true + $(IntDir) + + + true + + + $(OutDir)$(TargetName)$(TargetExt) + $(SolutionDir)\LifeCore\lib;$(REPO_DIR)\zlib-1.2.5\lib;$(REPO_DIR)\freetype-2.4.3\objs\win32\vc2008;$(DXSDK_DIR)\Lib\x86;%(AdditionalLibraryDirectories) + LifeCore_d.lib;freetype243_D.lib;d3dx11.lib + + + + + MaxSpeed + true + $(REPO_DIR)\zlib-1.2.5;$(REPO_DIR)\freetype-2.4.3\include;$(DXSDK_DIR)\Include;$(SolutionDir)/LifeCore/;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;LIFEENGINE_EXPORTS;%(PreprocessorDefinitions) + MultiThreadedDLL + true + true + + + Level3 + ProgramDatabase + true + + + $(OutDir)$(TargetName)$(TargetExt) + $(SolutionDir)\LifeCore\lib;$(REPO_DIR)\zlib-1.2.5\lib;$(REPO_DIR)\freetype-2.4.3\objs\win32\vc2008;$(DXSDK_DIR)\Lib\x86;%(AdditionalLibraryDirectories) + LifeCore.lib;freetype243.lib;d3dx11.lib + + + + + MaxSpeed + true + $(REPO_DIR)\zlib-1.2.5;$(REPO_DIR)\freetype-2.4.3\include;$(DXSDK_DIR)\Include;.\include;$(SolutionDir)\LifeCore\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;LIFEENGINE_EXPORTS;%(PreprocessorDefinitions) + MultiThreadedDLL + true + true + + + Level3 + ProgramDatabase + true + + + $(OutDir)$(TargetName)$(TargetExt) + $(SolutionDir)\LifeCore\lib;$(REPO_DIR)\zlib-1.2.5\lib;$(REPO_DIR)\freetype-2.4.3\objs\win32\vc2008;$(DXSDK_DIR)\Lib\x86;%(AdditionalLibraryDirectories) + LifeCore.lib;freetype243.lib;d3dx11.lib + + + + + + \ No newline at end of file diff --git a/LifeGraphics/Light.h b/LifeGraphics/Light.h new file mode 100644 index 0000000..3aa8171 --- /dev/null +++ b/LifeGraphics/Light.h @@ -0,0 +1,204 @@ +#ifndef Light_h__ +#define Light_h__ +#include "IGraphicsNode.h" +#include "math/Vector3D.h" +#include "math/Vector4D.h" +#include "camera/Frustum.h" + +namespace LifeGraphics +{ + enum LIGHT_TYPE + { + LT_POINT = 0, + LT_SPOT = 1, + LT_DIRECTIONAL = 2 + }; + class Light : public IGraphicsNode + { + public: + Light(LIGHT_TYPE lt) + : range(1), innerAngle(0.5f), outerAngle(0.6f), direction(0.0f, -1.0f, 0.0f), + position(0.0f,0.0f, 0.0f), up(0.0f, 1.0f, 0.0f), color(1.0f, 0.0f, 1.0f), + intensity(1.0f), isMatrixValid(false), castShadows(false) + { + lightType = lt; + } + Light() + : lightType(LT_POINT), range(1), innerAngle(0.5f), outerAngle(0.6f), direction(0.0f, -1.0f, 0.0f), + position(0.0f,0.0f, 0.0f), up(0.0f, 1.0f, 0.0f), color(1.0f, 0.0f, 1.0f), + intensity(1.0f), isMatrixValid(false), castShadows(false) + { + } + ~Light() + { + } + NodeType getType() + { + return NT_LIGHT; + } + void setDirection(const LifeMath::float3& dir) + { + direction = dir; + isMatrixValid = false; + //this->bSphere.center = this->_position; + //this->bSphere.radius = this->range; + } + + const LifeMath::float4x4& getMatrix() + { + fixupTransform(); + return matrix; + } + + const LifeMath::float4x4& getView() + { + fixupTransform(); + return mView; + } + const LifeMath::float4x4& getProj() + { + fixupTransform(); + return mProj; + } + LifeMath::float3 getDirection() + { + fixupTransform(); + return direction; + } + LifeMath::float3 getPosition() + { + fixupTransform(); + return position; + } + void setInnerAngle(float angle) + { + innerAngle = angle; + if(innerAngle > 3.1415f) + innerAngle = 3.1415f; + isMatrixValid = false; + } + float getInnerAngle() + { + return innerAngle; + } + void setOuterAngle(float angle) + { + outerAngle = angle; + if(outerAnglebSphere.radius = this->range; + isMatrixValid = false; + } + float getRange() + { + return range; + } + void update(float dt) + { + fixupTransform(); + } + LifeMath::float4x4 getMeshMatrix() + { + float baseRadius = tanf(outerAngle * 0.5f) * range * 1.1f; + LifeMath::float4x4 b; + Mat4x4Scaling(baseRadius, baseRadius, range, b); + + LifeMath::float3 right; + LifeMath::float3 orthoUp; + LifeMath::float3 dir = direction - position; + LifeMath::Vec3Normalize(&dir); + LifeMath::Vec3Cross(up, dir, right); + LifeMath::Vec3Cross(dir, right, orthoUp); + LifeMath::Vec3Normalize(&orthoUp); + + LifeMath::float4x4 r; + Mat4x4Identity(&r); + + r.M11 = right.X; + r.M12 = right.Y; + r.M13 = right.Z; + + r.M21 = orthoUp.X; + r.M22 = orthoUp.Y; + r.M23 = orthoUp.Z; + + r.M31 = dir.X; + r.M32 = dir.Y; + r.M33 = dir.Z; + + LifeMath::float3 pos = transform->GetPosition(); + r.M41 = pos.X; + r.M42 = pos.Y; + r.M43 = pos.Z; + + return b * r; + } + Frustum* getFrustum() + { + if(!isMatrixValid) + computeMatrix(); + return &frustum; + } + LIGHT_TYPE lightType; + + LifeMath::float3 color; + float specularPower; + float intensity; + bool castShadows; + private: + void fixupTransform() + { + if(position!= transform->GetPosition()) + { + position = transform->GetPosition(); + isMatrixValid = false; + this->bSphere.center = position; + this->bSphere.radius = range; + } + if(!isMatrixValid) + { + computeMatrix(); + } + } + void computeMatrix() + { + LifeMath::float3 dir = direction - position; + LifeMath::Vec3Normalize(&dir); + + float angle = LifeMath::Vec3Dot(&up, &dir); + if((angle>0.9f) | (angle<-0.9f)) + up = LifeMath::float3(0.0f,0.0f,1.0f); + else + up = LifeMath::float3(0.0f,1.0f,0.0f); + + LifeMath::Mat4x4LookAtLH(position,direction,up,mView); + LifeMath::Mat4x4PerspectiveFOV(outerAngle,1.0f,0.1f,range, mProj); + matrix = mView * mProj; + + frustum.UpdateFrustum(matrix); + + isMatrixValid = true; + } + LifeMath::float3 up; + bool isMatrixValid; + LifeMath::float4x4 matrix; + LifeMath::float4x4 mView; + LifeMath::float4x4 mProj; + float range; + LifeMath::float3 direction; + LifeMath::float3 position; + float innerAngle; + float outerAngle; + Frustum frustum; + }; +} +#endif // Light_h__ \ No newline at end of file diff --git a/LifeGraphics/MeshManager.cpp b/LifeGraphics/MeshManager.cpp new file mode 100644 index 0000000..a107664 --- /dev/null +++ b/LifeGraphics/MeshManager.cpp @@ -0,0 +1,19 @@ +#include "MeshManager.h" + +namespace LifeGraphics +{ + MeshManager::MeshManager() + { + } + MeshManager::~MeshManager() + { + } + IMesh* MeshManager::CreateBox(float sX, float sY, float sZ, bool bLockable) + { + IRenderer* render = CG::me()->GetRenderer(); + + LifeGraphics::Vertex::SVertexTbn vertex[24]; + + return 0; + } +} \ No newline at end of file diff --git a/LifeGraphics/MeshManager.h b/LifeGraphics/MeshManager.h new file mode 100644 index 0000000..c7a8206 --- /dev/null +++ b/LifeGraphics/MeshManager.h @@ -0,0 +1,19 @@ +#ifndef MeshManager_h__ +#define MeshManager_h__ + +#include "Singleton.h" +#include "IObjectMesh.h" +#include "render/IRenderer.h" +#include "Vertex.h" + +namespace LifeGraphics +{ + class MeshManager : public Singleton + { + public: + MeshManager(); + ~MeshManager(); + IMesh* CreateBox(float sX = 1.0f, float sY = 1.0f, float sZ = 1.0f, bool bLockable = false); + }; +} +#endif // MeshManager_h__ \ No newline at end of file diff --git a/LifeGraphics/PassGBuffer.h b/LifeGraphics/PassGBuffer.h new file mode 100644 index 0000000..17b9381 --- /dev/null +++ b/LifeGraphics/PassGBuffer.h @@ -0,0 +1,18 @@ +#ifndef PassGBuffer_h__ +#define PassGBuffer_h__ +#include "render/IRenderer.h" + +namespace LifeGraphics +{ + class PassGBuffer + { + public: + PassGBuffer() + { + } + ~PassGBuffer() + { + } + }; +} +#endif // PassGBuffer_h__ \ No newline at end of file diff --git a/LifeGraphics/Plugin.cpp b/LifeGraphics/Plugin.cpp new file mode 100644 index 0000000..74194d2 --- /dev/null +++ b/LifeGraphics/Plugin.cpp @@ -0,0 +1,34 @@ +#include "Plugin.h" +#include + +namespace LifeGraphics +{ + Plugin::Plugin(const std::string& fileName) + { + g_hDLL = ::LoadLibraryA(fileName.c_str()); + if(!g_hDLL) + throw std::runtime_error("Could not load library"); + + try + { + g_pfnGetPluginVersion = reinterpret_cast(::GetProcAddress(g_hDLL, "getPluginVersion")); + g_pfnGetEngineVersion = reinterpret_cast(::GetProcAddress(g_hDLL, "getEngineVersion")); + g_pfnRegisterPlugin = reinterpret_cast(::GetProcAddress(g_hDLL, "registerPlugin")); + //g_pfnGetPluginName = reinterpret_cast(::GetProcAddress(g_hDLL, "getPluginName")); + + if(!g_pfnGetPluginVersion || !g_pfnGetEngineVersion || !g_pfnRegisterPlugin) + throw std::runtime_error("Plugin is not valid"); + } + catch(...) + { + ::FreeModule(g_hDLL); + throw std::runtime_error("Plugin is not valid"); + } + } + + Plugin::~Plugin() + { + if(g_referenceCount<=0 && g_hDLL != NULL) + ::FreeModule(g_hDLL); + } +} \ No newline at end of file diff --git a/LifeGraphics/Plugin.h b/LifeGraphics/Plugin.h new file mode 100644 index 0000000..b7b98b5 --- /dev/null +++ b/LifeGraphics/Plugin.h @@ -0,0 +1,43 @@ +#ifndef Plugin_h__ +#define Plugin_h__ +#pragma once +#include "Build.h" +#include +#include "render/IRenderer.h" + +namespace LifeGraphics +{ + class PluginServer; + + class Plugin + { + public: + Plugin(const std::string& fileName); + ~Plugin(); + + int getEngineVersion(){ + return g_pfnGetEngineVersion(); + } + int getPluginVersion(){ + return g_pfnGetPluginVersion(); + } + void registerPlugin(PluginServer& server){ + return g_pfnRegisterPlugin(server); + } + /*LIFE_EXPORTS std::string getPluginName(){ + return g_pfnGetPluginName(); + }*/ + private: + typedef int (*fn_getEngineVersion)(void); + typedef int (*fn_getPluginVersion)(void); + typedef void (*fn_registerPlugin)(LifeGraphics::PluginServer&); + HMODULE g_hDLL; + int g_referenceCount; + + fn_getEngineVersion g_pfnGetEngineVersion; + fn_getPluginVersion g_pfnGetPluginVersion; + fn_registerPlugin g_pfnRegisterPlugin; + //fn_getPluginName g_pfnGetPluginName; + }; +} +#endif // Plugin_h__ \ No newline at end of file diff --git a/LifeGraphics/PluginServer.cpp b/LifeGraphics/PluginServer.cpp new file mode 100644 index 0000000..5f2a453 --- /dev/null +++ b/LifeGraphics/PluginServer.cpp @@ -0,0 +1,46 @@ +#include "PluginServer.h" +#include + +namespace LifeGraphics +{ + PluginServer::PluginServer() + { + } + + PluginServer::~PluginServer() + { + for(RendererVector::reverse_iterator it = g_rendererCollection.rbegin(); + it != g_rendererCollection.rend(); ++it) + { + delete *it; + } + + for(PluginMap::reverse_iterator it = g_loadedPlugins.rbegin(); + it != g_loadedPlugins.rend(); ++it) + { + delete it->second; + } + } + + HRESULT PluginServer::LoadPlugin(const std::string& name) + { + if(g_loadedPlugins.find(name) == g_loadedPlugins.end()) + { + Plugin* p = new Plugin(name); + p->registerPlugin(*this); + g_loadedPlugins.insert(PluginMap::value_type(name, p)); + + printf("%s\n", "Plugin successfully loaded!"); + + //std::string s = p.getPluginName(); + printf(g_rendererCollection.at(0)->GetRendererName().c_str()); + } + return S_OK; + } + + void PluginServer::RegisterRendererPlugin(IRenderer* renderer) + { + printf("%s\n", renderer->GetRendererName().c_str()); + g_rendererCollection.push_back(renderer); + } +} \ No newline at end of file diff --git a/LifeGraphics/PluginServer.h b/LifeGraphics/PluginServer.h new file mode 100644 index 0000000..33123e5 --- /dev/null +++ b/LifeGraphics/PluginServer.h @@ -0,0 +1,35 @@ +#ifndef PluginServer_h__ +#define PluginServer_h__ +#pragma once +#pragma warning (disable: 4251) +#define WIN32_LEAN_AND_MEAN +#include +#include +#include "Plugin.h" +#include "render/IRenderer.h" +#include +#include "Build.h" + +namespace LifeGraphics +{ + class LIFE_EXPORTS PluginServer + { + public: + PluginServer(); + ~PluginServer(); + void RegisterRendererPlugin(IRenderer* renderer); + HRESULT LoadPlugin(const std::string& name); + IRenderer* GetRenderer(int index) + { + printf(g_rendererCollection.at(0)->GetRendererName().c_str()); + return g_rendererCollection.at(index); + } + typedef std::vector RendererVector; + typedef std::map PluginMap; + private: + + RendererVector g_rendererCollection; + PluginMap g_loadedPlugins; + }; +} +#endif // PluginServer_h__ \ No newline at end of file diff --git a/LifeGraphics/Rectangle2D.h b/LifeGraphics/Rectangle2D.h new file mode 100644 index 0000000..c3f132c --- /dev/null +++ b/LifeGraphics/Rectangle2D.h @@ -0,0 +1,41 @@ +#ifndef Rectangle2D_h__ +#define Rectangle2D_h__ + +namespace LifeGraphics +{ + class Rectangle2D + { + public: + float minX; + float minY; + float maxX; + float maxY; + + Rectangle2D(float _minX, float _minY, float _maxX, float _maxY) + { + minX = _minX; + maxX = _maxX; + minY = _minY; + maxY = _maxY; + } + const float width() const + { + return maxX - minX; + } + const float height() const + { + return maxY - minY; + } + Rectangle2D() + { + minX = 0.0f; + maxX = 0.0f; + minY = 0.0f; + maxY = 0.0f; + } + ~Rectangle2D() + { + } + }; +} +#endif // Rectangle2D_h__ \ No newline at end of file diff --git a/LifeGraphics/RenderOperation.h b/LifeGraphics/RenderOperation.h new file mode 100644 index 0000000..a457ad1 --- /dev/null +++ b/LifeGraphics/RenderOperation.h @@ -0,0 +1,21 @@ +#ifndef RenderOperation_h__ +#define RenderOperation_h__ +#include "IMesh.h" + +namespace LifeGraphics +{ + class RenderOperation + { + public: + RenderOperation() + { + } + ~RenderOperation() + { + } + IMesh* mesh; + IShader* vertexShader, pixelShader; + int submesh; + }; +} +#endif // RenderOperation_h__ \ No newline at end of file diff --git a/LifeGraphics/RenderPass.h b/LifeGraphics/RenderPass.h new file mode 100644 index 0000000..b66bc58 --- /dev/null +++ b/LifeGraphics/RenderPass.h @@ -0,0 +1,10 @@ +#ifndef RenderPass_h__ +#define RenderPass_h__ +#include "render/IRenderer.h" + +#include "IScene.h" + +namespace LifeGraphics +{ +} +#endif // RenderPass_h__ \ No newline at end of file diff --git a/LifeGraphics/RenderQueue.h b/LifeGraphics/RenderQueue.h new file mode 100644 index 0000000..51108c5 --- /dev/null +++ b/LifeGraphics/RenderQueue.h @@ -0,0 +1,33 @@ +#ifndef RenderQueue_h__ +#define RenderQueue_h__ +#include +#include +#include "IGraphicsNode.h" +namespace LifeGraphics +{ + class RenderQueue + { + private: + std::queue container; + public: + RenderQueue(){} + ~RenderQueue(){} + int count() + { + return container.size(); + } + void push(IGraphicsNode* node) + { + container.push(node); + } + void pop() + { + return container.pop(); + } + IGraphicsNode* getCurrent() + { + return container.back(); + } + }; +} +#endif // RenderQueue_h__ \ No newline at end of file diff --git a/LifeGraphics/ResourceManager.cpp b/LifeGraphics/ResourceManager.cpp new file mode 100644 index 0000000..848ba08 --- /dev/null +++ b/LifeGraphics/ResourceManager.cpp @@ -0,0 +1,639 @@ +#include "ResourceManager.h" +#include "render/IMaterial.h" + + +namespace LifeGraphics +{ + struct VMFHEADER + { + int fileVersion; + int fileSignature; + int expPosition; + int expNormal; + int expTexcoord; + int expTangent; + int expBiNormal; + int expVertexColor; + int expSkin; + + int vertexCount; + int faceCount; + int bonesCount; + int vertexSize; + }; + + /*void ResourceManager::getMaterial(String& path, IMaterial** material) + { + pugi::xml_document doc; + char* data; + Core::IFile* file = getFileSystem()->GetFile(path); + Core::IDataStream* stm = file->OpenRead(); + data = new char[stm->length()]; + stm->Read(data, stm->length()); + + doc.load_buffer(data, stm->length()); + printf("%s\n", doc.child("LifeMaterial").attribute("Emissive").value()); + stm->Close(); + }*/ + ResourceManager::ResourceManager() + { + lastId = 0; + lastTimeUpdated = 0.0f; + updatePeriod = 5.0f; + LifeCore::RootFolder* root = new LifeCore::RootFolder(); + root->Name = String(_text("root")); + fileSystem = new LifeCore::IFileSystem(root); + fileSystem->StorageName = String(_text("root")); + + shaderDefines[EF_LIGHTING] = "DEF_LIGHTING"; + shaderDefines[EF_NORMAL_MAP] = "DEF_NORMAL_MAP"; + shaderDefines[EF_PARALLAX_MAP] = "DEF_PARALLAX_MAP"; + shaderDefines[EF_ENVIRONMENT_MAP] = "DEF_ENVIRONMENT_MAP"; + shaderDefines[EF_SPECULAR_MAP] = "DEF_SPECULAR_MAP"; + shaderDefines[EF_EMISSIVE_MAP] = "DEF_EMISSIVE_MAP"; + shaderDefines[EF_G_BUFFER_PREPASS] = "DEF_G_PREPASS"; + shaderDefines[EF_SKINNING] = "DEF_SKINNING"; + shaderDefines[EF_LINEAR_DEPTH] = "DEF_LINEAR_DEPTH"; + shaderDefines[EF_ALPHA_TEST] = "DEF_ALPHA_TEST"; + + shaderCompiler = new ShaderGraphCompilerHLSL(); + shaderFactory = IShaderGraphNodeFactory::me(); + IShaderFactorySource::Register(shaderFactory); + + IShaderGraph* defaultGraph = new IShaderGraph(shaderFactory); + + IShaderGraphNode* mat = defaultGraph->AddNode("Material"); + IShaderGraphNode* mat_diffuse = defaultGraph->AddNode("ConstFloat4"); + IShaderGraphNodePort* port_diffuse = mat->GetInputPortByName("Diffuse"); + port_diffuse->MakeLink(port_diffuse, mat_diffuse->GetOutputPortByID(0)); + + LifeCore::IProperty* diffuse; + if(mat_diffuse->GetPropertyByAttribute("Name", "Value", &diffuse)){ + + diffuse->Set(LifeMath::float4(0,1,0,1)); + } + + defaultMaterial = new IMaterial(defaultGraph, shaderCompiler); + } + ResourceManager::~ResourceManager() + { + } + void ResourceManager::CollectGarbage(float dt) + { + } + + void ResourceManager::ReleaseResources() + { + shaderDefines.clear(); + for(UINT i = 0; i< contentProviders.size(); i++) + { + delete contentProviders[i]; + } + delete fileSystem; + + textureCache.Release(); + vsCache.Release(); + psCache.Release(); + } + void ResourceManager::initialize() + { + } + long ResourceManager::NextID() + { + return lastId++; + } + void ResourceManager::SetCentralDirectory(const String& dir) + { + centralDir = dir; + LifeCore::IContentProvider* fsProvider = new LifeCore::IFSContentProvider(dir); + MountContentProvider(fsProvider, String(_text("root"))); + } + + IShader* ResourceManager::getShader(IMaterial* material, int adFlags, SHADER_TYPE type) + { + assert(false && "getShader is obsolete!\n"); + /*int flags = adFlags; + if(material->normalMapping) + flags |= EF_NORMAL_MAP; + if(material->lighting) + flags |= EF_LIGHTING; + if(material->environmentReflection) + flags |= EF_ENVIRONMENT_MAP; + if(material->bSpecularMapping) + flags |= EF_SPECULAR_MAP; + if(material->AlphaTest) + flags |= EF_ALPHA_TEST; + + if(type == ST_PS) + return GetPixelShader(flags); + else + return GetVertexShader(flags);*/ + return nullptr; + } + + IMaterial* ResourceManager::getDefaultMaterial() + { + return defaultMaterial; + assert(false && "getDefaultMaterial is obsolete!\n"); + /*if(!DefaultMaterial) + { + DefaultMaterial = new IMaterial(); + memset(DefaultMaterial,0, sizeof(IMaterial)); + DefaultMaterial->castShadows = false; + DefaultMaterial->diffuseMap = GetTexture(AS_STRING("root/Data/Textures/NoTex.dds")); + + setupMaterialShaders(EF_LIGHTING, DefaultMaterial); + }*/ + } + LifeCore::IFileSystem* ResourceManager::getFileSystem() + { + return fileSystem; + } + void ResourceManager::MountContentProvider(LifeCore::IContentProvider* prov, const String& mountPoint) + { + contentProviders.push_back(prov); + fileSystem->GetRootFolder()->AddFolder(prov->getFileSystem()->GetRootFolder()); + LifeCore::CLog::me()->Write(String(_text("Mount point is not used! SHOULD BE FIXED!\nResourceManager::MountContentProvider at"))); + LifeCore::CLog::me()->Write(mountPoint.c_str()); + } + texture_ptr ResourceManager::GetTexture(const String& path) + { + if(path == STR("")) + return nullptr; + String name = GetFileName(path, _text("/")); + texture_ptr ptr; + if(textureCache.GetResource(name, ptr)) + return ptr.ptr(); + else + { + LifeCore::IFile* texFile; + ResourceManager::me()->getFileSystem()->GetFile(path, &texFile); + LifeCore::IDataStreamPtr stm = texFile->OpenRead(); + ITexture2D* texture = CG::me()->GetRenderer()->LoadTexture2D(stm); + texture->Name = name; + texture_ptr texPtr(texture); + textureCache.push_back(texPtr); + return texPtr; + } + } + + void ResourceManager::ReadBitmap(String& path, void** pOut, IBitmapInfo** info) + { + LifeCore::IFile* file; + ResourceManager::me()->getFileSystem()->GetFile(path, &file); + LifeCore::IDataStreamPtr stm = file->OpenRead(); + + BITMAPFILEHEADER bitmapFileHeader; + BITMAPINFOHEADER bitmapInfoHeader; + + //read the bitmap file header + stm->Read(&bitmapFileHeader, sizeof(BITMAPFILEHEADER)); + + //verify that this is a bmp file by check bitmap id + if (bitmapFileHeader.bfType !=0x4D42) + { + return; + } + + //read the bitmap info header + stm->Read(&bitmapInfoHeader, sizeof(BITMAPINFOHEADER)); + + //move file point to the begging of bitmap data + stm->Seek(bitmapFileHeader.bfOffBits); + + //allocate enough memory for the bitmap image data + + char* data = new char[bitmapInfoHeader.biWidth * bitmapInfoHeader.biHeight * bitmapInfoHeader.biBitCount / 8]; + + //read in the bitmap image data + stm->Read(data, bitmapInfoHeader.biWidth * bitmapInfoHeader.biHeight * bitmapInfoHeader.biBitCount / 8); + + IBitmapInfo* i = new IBitmapInfo(); + i->Width = bitmapInfoHeader.biWidth; + i->Height = bitmapInfoHeader.biHeight; + i->Bpp = bitmapInfoHeader.biBitCount / 8; + + *(pOut) = data; + *(info) = i; + } + IShader* ResourceManager::GetPixelShader(int compileFlags) + { + String shaderPath(_text("Core.psh")); + char name[16]; + _itoa_s(compileFlags, name, 10); + String sName(name); + + //create defines list + std::vector defines; + + for(std::map::iterator it = shaderDefines.begin(); it != shaderDefines.end(); it++) + { + if((compileFlags & it->first) != 0) + { + defines.push_back(IMacroDefine(it->second)); + } + } + + resource_ptr ptr; + if(psCache.GetResource(sName, ptr)) + return ptr.ptr(); + else + { + LifeCore::IFile* file; + ResourceManager::me()->getFileSystem()->GetFile(STR("root/Data/Shaders/") + shaderPath, &file); + LifeCore::IDataStreamPtr stm = file->OpenRead(); + + IShader* s = CG::me()->GetRenderer()->CompilePixelShader(stm, &defines); + ptr.resetPtr(s); + + ptr->Name = sName; + psCache.push_back(ptr); + return ptr.ptr(); + } + defines.clear(); + } + IShader* ResourceManager::GetVertexShader(int compileFlags) + { + String shaderPath(_text("Core.vsh")); + char name[16]; + _itoa_s(compileFlags, name, 10); + String sName(name); + + //create defines list + std::vector defines; + + for(std::map::iterator it = shaderDefines.begin(); it != shaderDefines.end(); it++) + { + if((compileFlags & it->first) != 0) + { + defines.push_back(IMacroDefine(it->second)); + } + } + + resource_ptr ptr; + if(vsCache.GetResource(sName, ptr)) + return ptr.ptr(); + else + { + LifeCore::IFile* file; + ResourceManager::me()->getFileSystem()->GetFile(STR("root/Data/Shaders/") + shaderPath, &file); + LifeCore::IDataStreamPtr stm = file->OpenRead(); + + IShader* s = CG::me()->GetRenderer()->CompileVertexShader(stm, &defines); + ptr.resetPtr(s); + + ptr->Name = sName; + vsCache.push_back(ptr); + return ptr.ptr(); + } + defines.clear(); + } + + IMesh* ResourceManager::GetMesh(const String& path) + { + LifeCore::IFile* file; + ResourceManager::me()->getFileSystem()->GetFile(path, &file); + printf("Trololo %s\n", file->FileName.c_str()); + LifeCore::IDataStreamPtr stm = file->OpenRead(); + printf("Trololo %i\n", stm->Length()); + + VMFHEADER header; + stm->Read(&header, sizeof(VMFHEADER)); + + char* vertexData = new char[header.vertexCount * header.vertexSize]; + stm->Read(vertexData, header.vertexCount * header.vertexSize); + bool computeBoundingBox = true; + + LifeCore::BoundingBox3D bbox; + if(computeBoundingBox) + { + float* ptr = reinterpret_cast(vertexData); + float minX = ptr[0]; + float minY = ptr[1]; + float minZ = ptr[2]; + + float maxX = minX; + float maxY = minY; + float maxZ = minZ; + + for(int i = 0; i< header.vertexCount; i++) + { + ptr = reinterpret_cast(vertexData + (header.vertexSize * i)); + if(minX > ptr[0]) + minX = ptr[0]; + if(minY > ptr[1]) + minY = ptr[1]; + if(minZ > ptr[2]) + minZ = ptr[2]; + + if(maxX < ptr[0]) + maxX = ptr[0]; + if(maxY < ptr[1]) + maxY = ptr[1]; + if(maxZ < ptr[2]) + maxZ = ptr[2]; + } + + bbox.resize(LifeMath::float3(minX, minY, minZ), LifeMath::float3(maxX, maxY, maxZ)); + } + + int submeshCount= 0; + stm->Read(&submeshCount, 4); + + int* submeshData = new int[2*submeshCount]; + stm->Read(submeshData, 2*submeshCount*4); + + //IShader* vs = GetVertexShader(EFFECT_FLAGS::EF_LIGHTING); + //IShader* ps = GetPixelShader(EFFECT_FLAGS::EF_LIGHTING); + int isShortIndices = 0; + stm->Read(&isShortIndices, 4); + int indexSize = isShortIndices == 1 ? 2 : 4; + + char* indexData = new char[header.faceCount * 3 * indexSize]; + stm->Read(indexData, header.faceCount * 3 * indexSize); + + Skeleton sk; + if(header.expSkin) + { + sk.BonesCount = header.bonesCount; + + //add system root bone + Bone* b = new Bone(-1, "Root", -2); + QuatIdentity(b->LocalTransform.Rotation); + b->LocalTransform.Translation = LifeMath::float3(0.0f,0.0f,0.0f); + + QuatIdentity(b->BaseTransform.Rotation); + b->BaseTransform.Translation = LifeMath::float3(0.0f,0.0f,0.0f); + + Mat4x4Identity(&b->TransformMatrix); + Mat4x4Identity(&b->AbsoluteMatrix); + sk.AddBone(b); + + //read bones hierarchy + for (int j = 0; j < header.bonesCount; j++) + { + int bId; + stm->Read(&bId, sizeof(int)); + int pId; + stm->Read(&pId, sizeof(int)); + + char b_nChars[33]; + stm->Read(b_nChars, 33); + + std::string bName = std::string(b_nChars); + + Bone* b = new Bone(bId, bName, pId); + sk.AddBone(b); + } + + sk.rebuildLinks(); + + for (int j = 0; j < header.bonesCount; j++) + { + int bId; + stm->Read(&bId, sizeof(int)); + + LifeMath::float3 pos; + stm->Read(&pos, sizeof(LifeMath::float3)); + LifeMath::Quaternion rot; + stm->Read(&rot, sizeof(LifeMath::Quaternion)); + + BoneTransform transform; + transform.Translation = pos; + transform.Rotation = rot; + + transform.boneId = bId; + + Bone* cBone = sk.GetBone(bId); + + cBone->BaseTransform = transform; + + if (bId == 0) + { + cBone->LocalTransform = transform; + cBone->InitialTransform = transform; + cBone->BaseTransform = transform; + } + else + { + BoneTransform pt = cBone->Parent->BaseTransform; + + //LifeMath::float4x4 mParent = Matrix4x4.Invert(Matrix4x4.RotationQuaternion(pt.Rotation) * Matrix4x4.Translation(pt.Translation)); + LifeMath::float4x4 mParent = pt.GetMatrixInvert(); + LifeMath::float4x4 mBone = transform.GetMatrix(); + LifeMath::float4x4 mabs = mBone * mParent; + + LifeMath::float3 pos2, scl2; + LifeMath::Quaternion rot2; + + LifeMath::Mat4x4Decompose(mabs, pos2, scl2, rot2); + + BoneTransform bt; + bt.Translation = pos2; + bt.Rotation = rot2; + bt.boneId = bId; + + //cBone->LocalTransform = bt; + + cBone->LocalTransform = bt; + + cBone->InitialTransform = transform; + cBone->BaseTransform = transform; + } + } + } + + texture_ptr defaultTex = GetTexture("root/Data/Textures/basic_floor_s.dds"); + texture_ptr defaultNorm = GetTexture("root/Data/Textures/basic_floor_s_ddn.dds"); + + GeometrySubset* subsets = new GeometrySubset[submeshCount]; + + for(int i = 0; i< submeshCount; i++) + { + subsets[i].Offset = submeshData[i * 2 + 0]; + subsets[i].Length = submeshData[i * 2 + 1]; + + printf("Has skin: %i\n", header.expSkin); + /*if(header.expSkin) + setupMaterialShaders(EF_LIGHTING | EF_SKINNING, &subsets[i].material); + else + setupMaterialShaders(EF_LIGHTING, &subsets[i].material); + + subsets[i].material.diffuseMap = defaultTex; + subsets[i].material.normalMap = defaultNorm;*/ + } + + IVertexBuffer* vb = CG::me()->GetRenderer()->CreateVertexBuffer(header.vertexCount * header.vertexSize, vertexData, header.vertexSize); + delete[] vertexData; + + IIndexBuffer* ib = CG::me()->GetRenderer()->CreateIndexBuffer(header.faceCount * 3 * indexSize,indexData, isShortIndices); + delete[] indexData; + + delete[] submeshData; + + IInputElement* elements; + int numElements = 5; + if(header.expSkin) + { + numElements = 7; + elements = new IInputElement[numElements]; + elements[0] = IInputElement("POSITION", 0, 0, FORMAT_R32G32B32_FLOAT, 0, 0, 0); + elements[1] = IInputElement("TEXCOORD", 0, 12, FORMAT_R32G32_FLOAT, 0, 0, 0); + elements[2] = IInputElement("NORMAL", 0, 20, FORMAT_R32G32B32_FLOAT, 0, 0, 0); + elements[3] = IInputElement("TANGENT", 0, 32, FORMAT_R32G32B32_FLOAT, 0, 0, 0); + elements[4] = IInputElement("BINORMAL", 0, 44, FORMAT_R32G32B32_FLOAT, 0, 0, 0); + elements[5] = IInputElement("BLENDINDICES", 0, 56, FORMAT_R32G32B32A32_FLOAT, 0, 0, 0); + elements[6] = IInputElement("BLENDWEIGHT", 0, 72, FORMAT_R32G32B32A32_FLOAT, 0, 0, 0); + } + else + { + elements = new IInputElement[numElements]; + elements[0] = IInputElement("POSITION", 0, 0, FORMAT_R32G32B32_FLOAT, 0, 0, 0); + elements[1] = IInputElement("TEXCOORD", 0, 12, FORMAT_R32G32_FLOAT, 0, 0, 0); + elements[2] = IInputElement("NORMAL", 0, 20, FORMAT_R32G32B32_FLOAT, 0, 0, 0); + elements[3] = IInputElement("TANGENT", 0, 32, FORMAT_R32G32B32_FLOAT, 0, 0, 0); + elements[4] = IInputElement("BINORMAL", 0, 44, FORMAT_R32G32B32_FLOAT, 0, 0, 0); + } + + IShader* ds; + if(header.expSkin) + ds = GetVertexShader(EF_LIGHTING | EF_SKINNING); + else + ds = GetVertexShader(EF_LIGHTING); + + IInputLayout* layout = CG::me()->GetRenderer()->CreateInputLayout(elements, numElements, ds); + + delete[] elements; + + IMesh* mesh = new IMesh(vb, ib, subsets, layout, header.expSkin, bbox, file->FileName); + mesh->Id = NextID(); + mesh->subsetCount = submeshCount; + + if(header.expSkin) + { + mesh->skin.bonesCount = header.bonesCount; + sk.UpdateTransform(); + mesh->skin.skeleton = sk; + mesh->skin.controller.skeleton = &mesh->skin.skeleton; + } + return mesh; + } + void ResourceManager::setupMaterialShaders(int shaderFlags, IMaterial* material) + { + //printf("OMGNAH"); + // material->pixelShader = GetPixelShader(shaderFlags); + // material->vertexShader = GetVertexShader(shaderFlags); + assert(false && "setupMaterialShaders is obsolete!"); + //material->g_buffer_pass_ps = GetPixelShader(shaderFlags | EF_G_BUFFER_PREPASS); + //material->g_buffer_pass_vs = GetVertexShader(shaderFlags | EF_G_BUFFER_PREPASS); + } + Light* ResourceManager::CreatePointLight(const LifeMath::float3& position, float range) + { + Light* l = new Light(LT_POINT); + l->getTransform()->SetPosition(position); + l->setRange(range); + l->Id = NextID(); + return l; + } + + Light* ResourceManager::CreateDirectionalLight(const LifeMath::float3& direction) + { + Light* l = new Light(LT_DIRECTIONAL); + l->setDirection(direction); + l->Id = NextID(); + return l; + } + Light* ResourceManager::CreateSpotLight(const LifeMath::float3& position, const LifeMath::float3& direction, float range, float innerAngle, float outerAngle) + { + Light* l = new Light(LT_SPOT); + l->getTransform()->SetPosition(position); + l->setRange(range); + l->setInnerAngle(innerAngle); + l->setOuterAngle(outerAngle); + l->setDirection(direction); + l->Id = NextID(); + return l; + } + IShader* ResourceManager::CompileUserShaderPS(const String& path) + { + LifeCore::IFile* file; + if(SC_OK == ResourceManager::me()->getFileSystem()->GetFile(STR("root/Data/Shaders/") + path, &file)) + { + //CLog::me()-> + LifeCore::IDataStreamPtr stm = file->OpenRead(); + IShader* s = CG::me()->GetRenderer()->CompilePixelShader(stm); + s->Name = GetFileName(path, String(_text("/"))); + return s; + } + return 0; + } + IShader* ResourceManager::CompileUserShaderVS(const String& path) + { + LifeCore::IFile* file; + ResourceManager::me()->getFileSystem()->GetFile(STR("root/Data/Shaders/") + path, &file); + LifeCore::IDataStreamPtr stm = file->OpenRead(); + IShader* s = CG::me()->GetRenderer()->CompileVertexShader(stm); + s->Name = GetFileName(path, String(_text("/"))); + return s; + } + + IShader* ResourceManager::CompileUserShaderPS(const char* pData, int length) + { + IShader* s = CG::me()->GetRenderer()->CompilePixelShader(pData, length); + s->Name = STR("system_ps"); + return s; + } + IShader* ResourceManager::CompileUserShaderVS(const char* pData, int length) + { + IShader* s = CG::me()->GetRenderer()->CompileVertexShader(pData, length); + s->Name = STR("system_vs"); + return s; + } + + AnimationTrack* ResourceManager::LoadAnimatrionTrack(String& path) + { + LifeCore::IFile* file; + ResourceManager::me()->getFileSystem()->GetFile(path, &file); + LifeCore::IDataStreamPtr stm = file->OpenRead(); + + float sampleRate; + int framesCount, BonesCount; + + stm->Read(&BonesCount, sizeof(int)); + stm->Read(&framesCount, sizeof(int)); + + AnimationTrack* animationTrack = new AnimationTrack(framesCount); + + animationTrack->Name = GetFileName(path, String(_text("/"))); + + int sr; + stm->Read(&sr, sizeof(int)); + sampleRate = (float)sr; + + //add all frames + for (int j = 0; j < framesCount; j++) + { + AnimationFrame frame((float)j / sampleRate); + frame.name = j; + + //read bones keys + for (int i = 0; i < BonesCount; i++) + { + int bId; + stm->Read(&bId, sizeof(int)); + + BoneTransform transform; + stm->Read(&transform.Translation, sizeof(LifeMath::float3)); + + stm->Read(&transform.Rotation, sizeof(LifeMath::Quaternion)); + + transform.boneId = bId; + frame.addBoneTransform(transform); + } + animationTrack->setFrame(frame, j); + } + + animationTrack->TrackTime = (float)(framesCount-1) / sampleRate; + return animationTrack; + } +} \ No newline at end of file diff --git a/LifeGraphics/ResourceManager.h b/LifeGraphics/ResourceManager.h new file mode 100644 index 0000000..62055de --- /dev/null +++ b/LifeGraphics/ResourceManager.h @@ -0,0 +1,104 @@ +#ifndef RESOURCE_MANAGER_H__ +#define RESOURCE_MANAGER_H__ + +#include "LifeDebug.h" +#include "Singleton.h" +#include "LString.h" +#include "vfs/IFileSystem.h" +#include "vfs/RootFolder.h" +#include "render/ITexture2D.h" +#include "IMesh.h" +#include "vfs/fs_win/IFSContentProvider.h" +#include +#include +#include "CG.h" +#include "Types.h" +#include "TypesVideo.h" +#include "Light.h" +#include "IResourceCollection.h" +#include "Skeleton.h" +#include "AnimationTrack.h" +#include "pugixml.hpp" +#include "shaders/ShaderGraphCompilerHLSL.h" +#include "shaders/IShaderGraphNodeFactory.h" +#include "shaders/IShaderFactorySource.h" + +namespace LifeGraphics +{ + class IMaterial; + + struct IBitmapInfo + { + int Width; + int Height; + int Bpp; + }; + + enum EFFECT_FLAGS + { + EF_NONE = 0, + EF_LIGHTING = 1 << 0, + EF_NORMAL_MAP = 1 << 1, + EF_PARALLAX_MAP = 1 << 2, + EF_ENVIRONMENT_MAP = 1 << 3, + EF_SPECULAR_MAP = 1 << 4, + EF_EMISSIVE_MAP = 1 << 5, + EF_G_BUFFER_PREPASS = 1 << 6, + EF_SKINNING = 1 << 7, + EF_LINEAR_DEPTH = 1 << 8, + EF_ALPHA_TEST = 1 << 9 + }; + + class ResourceManager; + class ResourceManager : public Singleton + { + public: + + ResourceManager(); + ~ResourceManager(); + void SetCentralDirectory(const String& dir); + void initialize(); + LifeCore::IFileSystem* getFileSystem(); + void MountContentProvider(LifeCore::IContentProvider* prov, const String& mountPoint); + texture_ptr GetTexture(const String& path); + IShader* GetPixelShader(int flags); + IShader* GetVertexShader(int flags); + IMesh* GetMesh(const String& path); + Light* CreatePointLight(const LifeMath::float3& position, float range); + Light* CreateDirectionalLight(const LifeMath::float3& direction); + void setupMaterialShaders(int shaderFlags, IMaterial* material); + Light* CreateSpotLight(const LifeMath::float3& position, const LifeMath::float3& direction, float range, float innerAngle, float outerAngle); + IShader* CompileUserShaderPS(const String& path); + IShader* CompileUserShaderVS(const String& path); + + IShader* CompileUserShaderPS(const char* pData, int length); + IShader* CompileUserShaderVS(const char* pData, int length); + + AnimationTrack* LoadAnimatrionTrack(String& path); + void ReleaseResources(); + void ReadBitmap(String& path, void** pOut, IBitmapInfo** info); + void CollectGarbage(float dt); + IMaterial* getDefaultMaterial(); + void getMaterial(String& path, IMaterial** material); + + IShader* getShader(IMaterial* material, int adFlags, SHADER_TYPE type); + + private: + IShaderGraphCompiler* shaderCompiler; + IShaderGraphNodeFactory* shaderFactory; + IMaterial* defaultMaterial; + float lastTimeUpdated; + float updatePeriod; + String centralDir; + LifeCore::IFileSystem* fileSystem; + IResourceCollection textureCache; + IResourceCollection vsCache; + IResourceCollection psCache; + std::map shaderDefines; + std::vector contentProviders; + long lastId; + long NextID(); + }; +} + +#endif \ No newline at end of file diff --git a/LifeGraphics/ShadowMapManager.h b/LifeGraphics/ShadowMapManager.h new file mode 100644 index 0000000..9c407b6 --- /dev/null +++ b/LifeGraphics/ShadowMapManager.h @@ -0,0 +1,206 @@ +#ifndef ShadowMapManager_h__ +#define ShadowMapManager_h__ + +#include "render/IRenderer.h" +#include +#include "math/Vector4D.h" +#include "math/Vector2D.h" +#include "Rectangle2D.h" +#include "math/Matrix4x4.h" +#include "Light.h" +#include "Globals.h" +#include + +namespace LifeGraphics +{ + struct SHADOW_DATA + { + LifeMath::float4 uvOffset_uvScale; + LifeMath::float4 shadowMapTexelSize_dummy; + }; + + class ShadowMapManager + { + private: + IRenderer* renderer; + int shadowMapResolution; + std::vector texRegions; + std::vector viewRegions; + ITexture2D* depthBuffer; + ITexture2D* shadowBuffer; + IBlendState* restoreBlendState; + + LifeMath::float4x4 lightMatrices[4]; + int curLightIndex; + IBlendState* shadowBlendState; + + void ComputeDepthRegionDistribution(int lightCount) + { + texRegions.clear(); + viewRegions.clear(); + printf("SHOULD FIX RECTANGLES!!!!!!!!!!!!!!!!!!!!! NOT COORD! WIDTH!!!!!!!!!!!!!!! ComputeDepthRegionDistribution\n"); + switch (lightCount) + { + case 1: + //----------| + //| | + //| 1 | + //| | + //| | + //----------| + + texRegions.push_back(LifeMath::float4(0.0f, 0.0f, 1.0f, 1.0f)); + viewRegions.push_back(LifeMath::Rect2i(0, 0, shadowMapResolution, shadowMapResolution)); + break; + case 2: + //----------| + //| 1 | + //| | + //----------- + //| | + //| 2 | + //----------| + /*texRegions.push_back(LifeMath::float4(0.0f, 0.0f, 1.0f, 0.5f)); + viewRegions.push_back(Rectangle2D(0.0f, 0.0f, shadowMapResolution2, shadowMapResolution2 / 2)); + + texRegions.push_back(LifeMath::float4(0.0f, 0.5f, 1.0f, 0.5f)); + viewRegions.push_back(Rectangle2D(0.0f, shadowMapResolution2 / 2, shadowMapResolution2, shadowMapResolution2 / 2));*/ + + texRegions.push_back(LifeMath::float4(0.0f, 0.0f, 1.0f, 0.5f)); + viewRegions.push_back(LifeMath::Rect2i(0, 0, shadowMapResolution, shadowMapResolution / 2)); + + texRegions.push_back(LifeMath::float4(0.0f, 0.5f, 1.0f, 0.5f)); + viewRegions.push_back(LifeMath::Rect2i(0, shadowMapResolution / 2, shadowMapResolution, shadowMapResolution)); + + break; + case 3: + texRegions.push_back(LifeMath::float4(0.0f, 0.0f, 1.0f, 0.5f)); + viewRegions.push_back(LifeMath::Rect2i(0, 0, shadowMapResolution, shadowMapResolution / 2)); + + texRegions.push_back(LifeMath::float4(0.0f, 0.5f, 0.5f, 0.5f)); + viewRegions.push_back(LifeMath::Rect2i(0, shadowMapResolution / 2, shadowMapResolution / 2, shadowMapResolution)); + + texRegions.push_back(LifeMath::float4(0.5f, 0.5f, 0.5f, 0.5f)); + viewRegions.push_back(LifeMath::Rect2i(shadowMapResolution / 2, shadowMapResolution / 2, shadowMapResolution, shadowMapResolution)); + break; + case 4: + texRegions.push_back(LifeMath::float4(0.0f, 0.0f, 0.5f, 0.5f)); + viewRegions.push_back(LifeMath::Rect2i(0, 0, shadowMapResolution / 2, shadowMapResolution / 2)); + + texRegions.push_back(LifeMath::float4(0.5f, 0.0f, 0.5f, 0.5f)); + viewRegions.push_back(LifeMath::Rect2i(shadowMapResolution / 2, 0, shadowMapResolution / 2, shadowMapResolution / 2)); + + texRegions.push_back(LifeMath::float4(0.0f, 0.5f, 0.5f, 0.5f)); + viewRegions.push_back(LifeMath::Rect2i(0, shadowMapResolution / 2, shadowMapResolution / 2, shadowMapResolution / 2)); + + texRegions.push_back(LifeMath::float4(0.5f, 0.5f, 0.5f, 0.5f)); + viewRegions.push_back(LifeMath::Rect2i(shadowMapResolution / 2, shadowMapResolution / 2, shadowMapResolution / 2, shadowMapResolution / 2)); + break; + } + } + public: + IDepthStencilState* depthState; + IShader* rtdPs; + IShader* rtdPsAT; + IShader* rtdCubePs; + IShader* rtdCubePsAT; + ITextureSampler* samplerPCF; + ShadowMapManager() + :shadowMapResolution(1024) + { + for(int i = 0; i<4; i++) + { + texRegions.push_back(LifeMath::float4(1.0f, 1.0f, 1.0f, 1.0f)); + } + } + ~ShadowMapManager() + { + SAFE_DELETE(depthBuffer); + SAFE_DELETE(rtdPs); + SAFE_DELETE(shadowBlendState); + SAFE_DELETE(depthState); + } + SHADOW_DATA getShadowInfo(int id) + { + SHADOW_DATA data; + data.uvOffset_uvScale = texRegions[id]; + data.shadowMapTexelSize_dummy.X = (float)shadowMapResolution; + data.shadowMapTexelSize_dummy.Y = (float)shadowMapResolution; + return data; + } + void beginShadowRendering(int lightCount) + { + renderer->SetScissorRectangle(LifeMath::Rect2i(0, 0, shadowMapResolution, shadowMapResolution)); + curLightIndex = 0; + if (lightCount > 0) + { + if (lightCount > 4) + lightCount = 4; + ComputeDepthRegionDistribution(lightCount); + } + renderer->UnbindResourcesPS(); + + renderer->SetTargets(shadowBuffer, depthBuffer); + LifeMath::float4 color = LifeMath::float4(1.0f, 1.0f, 1.0f, 1.0f); + renderer->ClearDepthStencil(depthBuffer, 1.0f, 0); + //renderer->ClearBuffers(CF_DEPTH_BUFFER, 0x00000000, 1.0f, 0); + // renderer->ClearRenderTarget(shadowBuffer, &color); + //renderer->SetBlendState(shadowBlendState); + + renderer->SetDepthStencilState(depthState); + renderer->SetPSTextureSampler(samplerPCF, 2); + //renderer->SetPixelShader(rtdPs); + } + + void beginLightRender(Light* light) + { + lightMatrices[curLightIndex] = light->getMatrix(); + + // renderer->ClearBuffers(CF_DEPTH_BUFFER, 0x00000000, 1.0f, 0); + renderer->SetViewport(viewRegions[curLightIndex]); + + //renderer->SetViewport(viewRegions[curLightIndex], 0, 100000000.0f); + } + void endLightRender() + { + curLightIndex++; + } + + ITexture2D* getShadowMap() + { + return shadowBuffer; + } + + void Init(IRenderer* _renderer) + { + renderer = _renderer; + +#define MAX_LINEAR_DEPTH 1.e30f + /* ComparisonFunc = LESS; + Filter = COMPARISON_MIN_MAG_LINEAR_MIP_POINT; + AddressU = Border; + AddressV = Border; + BorderColor = float4(MAX_LINEAR_DEPTH, 0, 0, 0);*/ + + ISamplerDescription desc(Border, Border, Border, ComparisonMinMagLinearMipPoint, COMPARISON_LESS); + samplerPCF = renderer->CreateTextureSampler(&desc); + + depthBuffer = CG::me()->GetRenderer()->CreateDepthTarget(shadowMapResolution, shadowMapResolution, FORMAT_R32_TYPELESS); + + shadowBuffer = CG::me()->GetRenderer()->CreateRenderTarget(shadowMapResolution, shadowMapResolution, FORMAT_R32_FLOAT); + + rtdPs = ResourceManager::me()->CompileUserShaderPS(String(_text("RTD_PCF.psh"))); + rtdPsAT = ResourceManager::me()->CompileUserShaderPS(String(_text("RTD_PCF_AT.psh"))); + + rtdCubePs = ResourceManager::me()->CompileUserShaderPS(String(_text("RTD_PCF_CUBE.psh"))); + rtdCubePsAT = ResourceManager::me()->CompileUserShaderPS(String(_text("RTD_PCF_AT.psh"))); + + shadowBlendState = CG::me()->GetRenderer()->CreateBlendState(false, false, BLEND_OP_ADD, BLEND_ZERO, BLEND_ONE, 0); + + restoreBlendState = CG::me()->GetRenderer()->CreateBlendState(true, false, BLEND_OP_ADD, BLEND_ONE, BLEND_ZERO, 1 | 2 | 4 | 8); + + depthState = renderer->CreateDSState(true, true, COMPARISON_LESS); + } + }; +} +#endif // ShadowMapManager_h__ \ No newline at end of file diff --git a/LifeGraphics/Skeleton.h b/LifeGraphics/Skeleton.h new file mode 100644 index 0000000..689b29e --- /dev/null +++ b/LifeGraphics/Skeleton.h @@ -0,0 +1,117 @@ +#ifndef Skeleton_h__ +#define Skeleton_h__ +#include +#include "Bone.h" +#include "AnimationTrack.h" +#define _CRTDBG_MAP_ALLOC +#include +#include + +namespace LifeGraphics +{ + class Skeleton + { + public: + int maxBoneID; + int BonesCount; + std::map Bones; + + Skeleton() + :maxBoneID(0) + { + } + + Bone* AddBone(Bone* bone) + { + Bones[bone->ID] = bone; + return bone; + } + + void rebuildLinks() + { + //clear all dependencies + for(std::map::iterator it = Bones.begin(); it != Bones.end(); it++) + { + it->second->clearChilds(); + it->second->Parent = 0; + } + //rebuild hierarchy links + for(std::map::iterator it = Bones.begin(); it != Bones.end(); it++) + { + if(it->second->ParentId != -2) + { + it->second->Parent = GetBone(it->second->ParentId); + it->second->Parent->AddChild(it->second); + } + } + } + + void UpdateTransform() + { + for (int i = 0; i < BonesCount; i++) + { + LifeMath::float4x4 BoneLocal; + LifeMath::float4x4 BoneBase; + Bone* bone = Bones[i]; + + BoneLocal = bone->LocalTransform.GetMatrix(); + + BoneBase = bone->BaseTransform.GetMatrix(); + BoneBase = Mat4x4Invert(&BoneBase); + + //BoneLocal = BoneBase * BoneLocal; + + if (bone->Parent) + { + //bone->TransformMatrix = BoneLocal * bone->Parent->AbsoluteMatrix; + bone->TransformMatrix = BoneLocal * bone->Parent->TransformMatrix; + } + else + { + bone->TransformMatrix = BoneLocal; + } + //bone->AbsoluteMatrix = BoneBase * bone->TransformMatrix; + bone->AbsoluteMatrix = BoneBase* bone->TransformMatrix; + bone->Update(); + } + } + + LifeMath::float4x4 BuildMatrix(BoneTransform* transform) + { + LifeMath::float4x4 rot; + LifeMath::float4x4 trans; + + Mat4x4RotationQuaternion(transform->Rotation, rot); + Mat4x4Translation(transform->Translation, trans); + + return rot*trans; + } + + void GetMatrices(int* boneIndices, int indexCount, LifeMath::float4x4* result) + { + for (int i = 0; i < indexCount; i++) + result[i] = GetBone(boneIndices[i])->AbsoluteMatrix; + } + + void GetMatrices(LifeMath::float4x4* result) + { + for (int i = 0; i < BonesCount; i++) + result[i] = GetBone(i)->AbsoluteMatrix; + } + + Bone* GetBone(int boneID) + { + return Bones[boneID]; + } + + Bone* GetBone(String& name) + { + for(std::map::iterator it = Bones.begin(); it != Bones.end(); it++) + { + if (it->second->BoneName == name) + return it->second; + } + } + }; +} +#endif // Skeleton_h__ \ No newline at end of file diff --git a/LifeGraphics/SkinInfo.h b/LifeGraphics/SkinInfo.h new file mode 100644 index 0000000..dda6424 --- /dev/null +++ b/LifeGraphics/SkinInfo.h @@ -0,0 +1,23 @@ +#ifndef SkinInfo_h__ +#define SkinInfo_h__ +#include "Skeleton.h" +#include "AnimationController.h" + +namespace LifeGraphics +{ + class SkinInfo + { + public: + Skeleton skeleton; + AnimationController controller; + int bonesCount; + SkinInfo() + :bonesCount(0) + { + } + ~SkinInfo() + { + } + }; +} +#endif // SkinInfo_h__ \ No newline at end of file diff --git a/LifeGraphics/Sky.h b/LifeGraphics/Sky.h new file mode 100644 index 0000000..a33172e --- /dev/null +++ b/LifeGraphics/Sky.h @@ -0,0 +1,397 @@ +#ifndef Sky_h__ +#define Sky_h__ +#include "Globals.h" +#include "DayTime.h" +#include "math/LifeMath.h" + +namespace LifeGraphics +{ + struct SkyVertex +{ + LifeMath::float3 Position; + LifeMath::float2 Texcoord; + SkyVertex() + { + } + SkyVertex(LifeMath::float3& _pos, LifeMath::float2& _tex) + { + Position = _pos; + Texcoord = _tex; + } +}; + +struct cbRenderData +{ + LifeMath::float4x4 View; + LifeMath::float4x4 Proj; + LifeMath::float4x4 ViewProj; + LifeMath::float3 SunDirection; + float dummy; +}; +class Sky +{ +private: + int DomeN; + int DVSize; + int DISize; + int MrlTexSize; + int MrlTexSizeHalf; + + LifeMath::float3 SunrizeDirection; + LifeMath::float3 ZenithDirection; + LifeMath::float3 waveLengths; + LifeMath::float3 invWaveLengths; + LifeMath::float3 waveLengthsMie; + + IVertexBuffer* skyVb; + IIndexBuffer* skyIb; + + IShader* VS_Render; + IShader* VS_Update; + IShader* PS_Render; + IShader* PS_Update; + + IConstantBuffer* cbUpdate; + IConstantBuffer* cbRender; + + ITexture2D* skyDepth; + + IInputLayout* vertexLayout; + + DayTime Time; + DayTime SunsetTime; + DayTime SunrizeTime; + DayTime MidDayTime; + DayTime MidNightTime; + DayTime DayLength; + DayTime NightLength; + DayTime TimeStep; + + int DayPhase; + + float fExposure; + float updateTimeElapsed; + float StarsIntensity; + float updatePeriod; + bool ShouldUpdateData; + + IDepthStencilState* dsZEnable; + IDepthStencilState* dsZDisable; + + void RecomputeData() + { + SunrizeDirection = LifeMath::float3(-1,0,0); + ZenithDirection = LifeMath::float3(0,1,0); + DayLength = SunsetTime - SunrizeTime; + NightLength = SunrizeTime - SunsetTime; + MidNightTime = SunsetTime + (NightLength / 2); + MidDayTime = SunrizeTime + (DayLength / 2); + + if (Time >= SunrizeTime && Time <= MidDayTime) + DayPhase = 0; + else if (Time >= MidDayTime && Time <= SunsetTime) + DayPhase = 1; + else if (MidNightTime - SunsetTime >= Time - SunsetTime) + DayPhase = 2; + else + DayPhase = 3; + float lamt = 0; + switch (DayPhase) + { + case 0: + lamt = (float)(Time - SunrizeTime).ToSeconds() + / (float)(MidDayTime - SunrizeTime).ToSeconds(); + SunDirection = Vec3Lerp(SunrizeDirection, ZenithDirection, lamt); + LifeMath::Vec3Normalize(&SunDirection); + StarsIntensity = 1.0f - lamt; + break; + case 1: + lamt = (float)(Time - MidDayTime).ToSeconds() + / (float)(SunsetTime - MidDayTime).ToSeconds(); + SunDirection = Vec3Lerp(ZenithDirection, -SunrizeDirection, lamt); + LifeMath::Vec3Normalize(&SunDirection); + StarsIntensity = lamt; + break; + case 2: + lamt = (float)(Time - SunsetTime).ToSeconds() + / (float)(MidNightTime - SunsetTime).ToSeconds(); + SunDirection = Vec3Lerp(-SunrizeDirection, -ZenithDirection, lamt); + LifeMath::Vec3Normalize(&SunDirection); + StarsIntensity = 1.0f; + break; + case 3: + lamt = (float)(Time - MidNightTime).ToSeconds() + / (float)(SunrizeTime - MidNightTime).ToSeconds(); + SunDirection = Vec3Lerp(-ZenithDirection, SunrizeDirection, lamt); + LifeMath::Vec3Normalize(&SunDirection); + StarsIntensity = 1.0f; + break; + } + StarsIntensity = pow(StarsIntensity,10); + + invWaveLengths.X = 1.0f / pow(waveLengths.X, 4.0f); + invWaveLengths.Y = 1.0f / pow(waveLengths.Y, 4.0f); + invWaveLengths.Z = 1.0f / pow(waveLengths.Z, 4.0f); + + waveLengthsMie.X = pow(waveLengths.X, -0.84f); + waveLengthsMie.Y = pow(waveLengths.Y, -0.84f); + waveLengthsMie.Z = pow(waveLengths.Z, -0.84f); + } + + void GenerateDome() + { + int Latitude = DomeN / 2; + int Longitude = DomeN; + DVSize = Longitude * Latitude; + DISize = (Longitude - 1) * (Latitude - 1) * 2; + DVSize *= 2; + DISize *= 2; + + IInputElement skyElements[] = + { + IInputElement("POSITION", 0, 0, FORMAT_R32G32B32_FLOAT, 0, 0, 0), + IInputElement("TEXCOORD", 0, 12, FORMAT_R32G32_FLOAT, 0, 0, 0), + }; + + vertexLayout = CG::me()->GetRenderer()->CreateInputLayout(skyElements, 2, VS_Render); + + SkyVertex* domeVerts = new SkyVertex[DVSize]; + + // Fill Vertex Buffer + int DomeIndex = 0; + for (int i = 0; i < Longitude; i++) + { + float MoveXZ = 100.0f * ((float)i / (float)(Longitude - 1)) * 3.141592f / 180.0f; + + for (int j = 0; j < Latitude; j++) + { + float MoveY = 3.1415f * (float)j / (float)(Latitude - 1); + + domeVerts[DomeIndex] = SkyVertex(); + domeVerts[DomeIndex].Position.X = (sin(MoveXZ) * cos(MoveY)); + domeVerts[DomeIndex].Position.Y = cos(MoveXZ); + domeVerts[DomeIndex].Position.Z = (sin(MoveXZ) * sin(MoveY)); + + domeVerts[DomeIndex].Position *= 10.0f; + + domeVerts[DomeIndex].Texcoord.X = 0.5f / (float)Longitude + (float)i / (float)Longitude; + domeVerts[DomeIndex].Texcoord.Y = 0.5f / (float)Latitude + (float)j / (float)Latitude; + + DomeIndex++; + } + } + for (int i = 0; i < Longitude; i++) + { + float MoveXZ = 100.0f * ((float)i / (float)(Longitude - 1)) * 3.1415f / 180.0f; + + for (int j = 0; j < Latitude; j++) + { + float MoveY = (3.1415f * 2.0f) - (3.1415f * (float)j / (float)(Latitude - 1)); + + domeVerts[DomeIndex] = SkyVertex(); + domeVerts[DomeIndex].Position.X = (sin(MoveXZ) * cos(MoveY)); + domeVerts[DomeIndex].Position.Y = cos(MoveXZ); + domeVerts[DomeIndex].Position.Z = (sin(MoveXZ) * sin(MoveY)); + + domeVerts[DomeIndex].Position *= 10.0f; + + domeVerts[DomeIndex].Texcoord.X = 0.5f / (float)Longitude + (float)i / (float)Longitude; + domeVerts[DomeIndex].Texcoord.Y = 0.5f / (float)Latitude + (float)j / (float)Latitude; + + DomeIndex++; + } + } + + // Fill index buffer + USHORT* ib = new USHORT[DISize * 3]; + int index = 0; + for (USHORT i = 0; i < Longitude - 1; i++) + { + for (short j = 0; j < Latitude - 1; j++) + { + ib[index++] = (USHORT)(i * Latitude + j); + ib[index++] = (USHORT)((i + 1) * Latitude + j); + ib[index++] = (USHORT)((i + 1) * Latitude + j + 1); + + ib[index++] = (USHORT)((i + 1) * Latitude + j + 1); + ib[index++] = (USHORT)(i * Latitude + j + 1); + ib[index++] = (USHORT)(i * Latitude + j); + } + } + USHORT Offset = (USHORT)(Latitude * Longitude); + for (USHORT i = 0; i < Longitude - 1; i++) + { + for (USHORT j = 0; j < Latitude - 1; j++) + { + ib[index++] = (USHORT)(Offset + i * Latitude + j); + ib[index++] = (USHORT)(Offset + (i + 1) * Latitude + j + 1); + ib[index++] = (USHORT)(Offset + (i + 1) * Latitude + j); + + ib[index++] = (USHORT)(Offset + i * Latitude + j + 1); + ib[index++] = (USHORT)(Offset + (i + 1) * Latitude + j + 1); + ib[index++] = (USHORT)(Offset + i * Latitude + j); + } + } + + skyIb = CG::me()->GetRenderer()->CreateIndexBuffer(DISize * 3 * sizeof(USHORT), ib, TRUE); + skyVb = CG::me()->GetRenderer()->CreateVertexBuffer(DVSize * sizeof(SkyVertex), domeVerts, sizeof(SkyVertex)); + } + +public: + LifeMath::float3 SunDirection; + ITexture2D* mieTex; + ITexture2D* rayleighTex; + Sky() + :DayPhase(0), fExposure(-2.0f), updateTimeElapsed(0.0f), updatePeriod(2.0f), ShouldUpdateData(true) + { + waveLengths = LifeMath::float3(0.65f, 0.57f, 0.475f); + TimeStep = DayTime(0, 0, 1); + } + ~Sky() + { + SAFE_DELETE(skyVb); + SAFE_DELETE(skyIb); + + SAFE_DELETE(cbUpdate); + SAFE_DELETE(cbRender); + SAFE_DELETE(dsZDisable); + SAFE_DELETE(dsZEnable); + + SAFE_DELETE(vertexLayout); + SAFE_DELETE(VS_Render); + // SAFE_DELETE(VS_Update); + SAFE_DELETE(PS_Render); + SAFE_DELETE(PS_Update); + SAFE_DELETE(mieTex); + SAFE_DELETE(rayleighTex); + SAFE_DELETE(skyDepth); + } + void Build() + { + cbUpdate = CG::me()->GetRenderer()->CreateConstantBuffer(sizeof(LifeMath::float4)); + cbRender = CG::me()->GetRenderer()->CreateConstantBuffer(sizeof(cbRenderData)); + + dsZDisable = CG::me()->GetRenderer()->CreateDSState(false,false, COMPARISON_NEVER); + dsZEnable = CG::me()->GetRenderer()->CreateDSState(true,true, COMPARISON_LESS); + + VS_Render = ResourceManager::me()->CompileUserShaderVS(String(_text("Sky.vsh"))); + PS_Render = ResourceManager::me()->CompileUserShaderPS(String(_text("Sky_render.psh"))); + PS_Update = ResourceManager::me()->CompileUserShaderPS(String(_text("Sky_update.psh"))); + + SunsetTime = DayTime(18, 2, 0); + SunrizeTime = DayTime(7, 10, 0); + Time = DayTime(12, 40, 0); + + RecomputeData(); + + MrlTexSize = 512; + MrlTexSizeHalf = MrlTexSize / 2; + + DomeN = 32; + GenerateDome(); + + mieTex = CG::me()->GetRenderer()->CreateRenderTarget(MrlTexSize, MrlTexSizeHalf, FORMAT_R16G16B16A16_FLOAT); + rayleighTex = CG::me()->GetRenderer()->CreateRenderTarget(MrlTexSize, MrlTexSizeHalf, FORMAT_R16G16B16A16_FLOAT); + skyDepth = CG::me()->GetRenderer()->CreateDepthTarget(MrlTexSize, MrlTexSizeHalf, FORMAT_D16_UNORM); + + //g_screenQuad = new Quad(0, 0, MrlTexSize, MrlTexSizeHalf, true); + } + void SetDayTime(const DayTime& time) + { + Time = time; + RecomputeData(); + } + + void Update(float dt) + { + updateTimeElapsed += dt; + if (updateTimeElapsed >= updatePeriod) + { + updateTimeElapsed = 0.0f; + ShouldUpdateData = true; + } + Time.Add((int)(dt * (float)TimeStep.ToSeconds())); + RecomputeData(); + } + + void Render(ITexture2D* sceneRt, ITexture2D* sceneDt, Camera* cam) + { + IRenderer* renderer = CG::me()->GetRenderer(); + renderer->SetDepthStencilState(dsZDisable); + + //Update mie and rayleigh textures + if (ShouldUpdateData) + { + ITexture2D* tex[] = {rayleighTex, mieTex}; + + renderer->SetTargets(tex, 2, skyDepth); + renderer->SetPixelShader(PS_Update); + + cbUpdate->Update(&SunDirection, sizeof(SunDirection)); + renderer->SetPSConstantBuffer(cbUpdate, 1); + + renderer->SetViewport(LifeMath::Rect2i(0,0, MrlTexSize, MrlTexSizeHalf)); + renderer->RenderToTextureNoPs(); + + renderer->UnbindRenderTargets(); + ShouldUpdateData = false; + } + + renderer->SetViewport(LifeMath::Rect2i(0,0, sceneRt->Width(), sceneRt->Height())); + + renderer->SetVertexShader(VS_Render); + renderer->SetPixelShader(PS_Render); + + float starIntensity = 0.7f; + float g = -0.990f; + float g2 = (-0.990f) * (-0.990f); + + LifeMath::float4x4 sm; + Mat4x4LookAtLH(LifeMath::float3(0, 0, 0), cam->getDirection(), LifeMath::float3(0, 1, 0), sm); + + cbRenderData data; + data.View = sm; + data.Proj = cam->getProj(); + data.ViewProj = data.View * data.Proj; + data.SunDirection = SunDirection; + + cbRender->Update(&data, sizeof(cbRenderData)); + renderer->SetPSConstantBuffer(cbRender, 1); + renderer->SetVSConstantBuffer(cbRender, 1); + + renderer->SetTargets(sceneRt, sceneDt); + + ////PS_Render.SetFloat("fExposure", fExposure); + ////PS_Render.SetFloat("starIntensity", StarsIntensity); + + ////PS_Render.SetTexture("starSampler", starTex); + ////PS_Render.SetTexture("mieSampler", mieTex); + ////PS_Render.SetTexture("rayleighSampler", rayleighTex); + /*CG.Engine.Renderer.SetTextureFilterMin(TextureFilter.Point, 2); + CG.Engine.Renderer.SetTextureFilterMip(TextureFilter.Point, 2); + CG.Engine.Renderer.SetTextureFilterMag(TextureFilter.Point, 2);*/ + ////PS_Render.SetFloat3("v3SunDir", SunDirection); + + renderer->SetPSTexture(rayleighTex,0); + renderer->SetPSTexture(mieTex,1); + + ////VS_Render.SetMatrix("matWorld", Matrix4x4.Identity); + ////VS_Render.SetMatrix("matView", sm); + ////VS_Render.SetMatrix("matProjection", renderer.MainCamera.ProjectionMatrix); + + renderer->SetInputLayout(vertexLayout); + + renderer->SetIndexBuffer(skyIb); + renderer->SetVertexBuffer(skyVb); + + renderer->SetPrimitiveTopology(PT_TRIANGLE_LIST); + + renderer->DrawIndexed(0, DISize); + + // renderer.DrawIndexedPrimitives(IPrimitiveTopology.TriangleList, 0, 0, SkyGeometry.NumberVertices, SkyGeometry.Subsets[0].Offset, SkyGeometry.Subsets[0].FaceCount); + + renderer->SetDepthStencilState(dsZEnable); + } +}; +} +#endif // Sky_h__ \ No newline at end of file diff --git a/LifeGraphics/Terrain.h b/LifeGraphics/Terrain.h new file mode 100644 index 0000000..d6b25dd --- /dev/null +++ b/LifeGraphics/Terrain.h @@ -0,0 +1,924 @@ +#ifndef Terrain_h__ +#define Terrain_h__ +#include "Globals.h" +#include "math/LifeMath.h" +#include "camera/Camera.h" +#include "ResourceManager.h" +#include + +namespace LifeGraphics +{ + enum TerrainDimention +{ + TD_X128 = 128, + TD_X256 = 256, + TD_X512 = 512, + TD_X1024 = 1024, + TD_X2048 = 2048, + TD_X4096 = 4096, + TD_X8192 = 8192 +}; + +struct TerraVertex +{ + int index; + char nX; + char nZ; + USHORT height; + + TerraVertex() + { + index = 0; + nX = nZ = 0; + height = 0; + } + + TerraVertex(int _index, char _nx, char _nz, USHORT _height) + { + index = _index; + nX = _nx; + nZ = _nz; + height = _height; + } +}; +struct TerraChunk +{ + int LOD; + int Offset; + LifeCore::BoundingBox3D BoundingBox; + IIndexBuffer* IB; + int Column, Row; + bool Visible; + int NumVerts; +}; + +struct LOD +{ + IIndexBuffer* Top; + IIndexBuffer* Bottom; + IIndexBuffer* Left; + IIndexBuffer* Right; + + IIndexBuffer* TopLeft; + IIndexBuffer* TopRight; + IIndexBuffer* BottomLeft; + IIndexBuffer* BottomRight; + + IIndexBuffer* full; + IIndexBuffer* allSides; +}; + +#define MAX_VISIBLE_DISTANCE 1025 //must be n ^ 2 + 1 + +struct TERRA_DATA +{ + float size; + LifeMath::float3 sunDir; +}; +class Terrain +{ +public: + std::vector lods; + IVertexBuffer* vb; + int vertexCount; + IInputLayout* layout; + TerraChunk* chunks; + IShader* terraVs; + IShader* terraPs; + + IConstantBuffer* terraCb; + + IShader* terraVs_g; + IShader* terraPs_g; + + texture_ptr diffuse; + IRasterizerState* solidState; + IRasterizerState* wireState; + void* data; + IBitmapInfo* info; + USHORT* heightMap; + + int TerrainWidth; + + std::vector visibleChunks; + + static INLINE int fround(float val) + { + return static_cast(floor(val + 0.5f)); + } + + static INLINE float clampFloat(float val, float min, float max) + { + if(val < min)return min; + if(val > max)return max; + return val; + } + + static INLINE UINT blendrgb(UINT d, UINT s, UINT a) + { + const UINT dstrb = d & 0xFF00FF; + const UINT dstg = d & 0xFF00; + + const UINT srcrb = s & 0xFF00FF; + const UINT srcg = s & 0xFF00; + + UINT drb = srcrb - dstrb; + UINT dg = srcg - dstg; + + drb *= a; + dg *= a; + drb >>= 8; + dg >>= 8; + + UINT rb = (drb + dstrb) & 0xFF00FF; + UINT g = (dg + dstg) & 0xFF00; + + return rb | g; + } + + static INLINE UINT sampleTex(void* tex, IBitmapInfo* info, float tX, float tY) + { + tX = clampFloat(tX, 0.0f, 1.0f); + tY = clampFloat(tY, 0.0f, 1.0f); + + float texelW = 1.0f / (float)(info->Width); + float texelH = 1.0f / (float)(info->Height); + + // tX *= (1.0f - texelW * 2.0f); + // tY *= (1.0f - texelH * 2.0f); + + int x = fround((tX * (float)(info->Width))); + int y = fround((tY * (float)(info->Height))); + UCHAR* ptr = reinterpret_cast(tex); + + float pX = tX * info->Width; + float pY = tY * info->Height; + + UINT frX = static_cast((pX - floorf(pX))* 255.0f); + UINT frY = static_cast((pY - floorf(pY))* 255.0f); + + UINT* ptr2 = reinterpret_cast(tex); + + UINT color; + if(info->Bpp < 4) + { + UINT tl = 0xff000000; + UINT tr = 0xff000000; + UINT rl = 0xff000000; + UINT rr = 0xff000000; + + memcpy(&tl, ptr+ (y*info->Width + x)* info->Bpp, 3); + memcpy(&tr, ptr+ (y*info->Width + x + 1)* info->Bpp, 3); + memcpy(&rl, ptr+ ((y + 1)*info->Width + x)* info->Bpp, 3); + memcpy(&rr, ptr+ ((y + 1)*info->Width + x + 1)* info->Bpp, 3); + + color = blendrgb ( blendrgb ( tl, tr, frX), blendrgb ( rl, rr, frX), frY); + } + + return color; + } + +#define MAX_HEIGHT 128 +#define AS_FLOAT(ololo) ololo##.0f + + Terrain(TerrainDimention dim, int chunkSize = 64, int maxVisibleRange = 100) + { + terraCb = CG::me()->GetRenderer()->CreateConstantBuffer(sizeof(TERRA_DATA)); + diffuse = ResourceManager::me()->GetTexture(String(_text("root/Data/Textures/Terrain/shortGrass.png"))); + + ResourceManager::me()->ReadBitmap(String(_text("root/Data/Textures/hm.bmp")), &data, &info); + + terraVs = ResourceManager::me()->CompileUserShaderVS(String(_text("Terrain.vsh"))); + terraPs = ResourceManager::me()->CompileUserShaderPS(String(_text("Terrain.psh"))); + + terraVs_g = ResourceManager::me()->CompileUserShaderVS(String(_text("Terrain_g.vsh"))); + terraPs_g = ResourceManager::me()->CompileUserShaderPS(String(_text("Terrain_g.psh"))); + + visibleCunkOffset = maxVisibleRange / chunkSize; + TerrainWidth = (int)dim; + ChunkWidth = chunkSize; + + //Generate LOD IndexBuffers + //------------------------------------LOD_1------------------- + LOD1Full = GenerateLOD(chunkSize, 1, false, false, false, false); + LOD1TrimAllSides = GenerateLOD(chunkSize, 1, true, true, true, true); + LOD1TrimNorthEast = GenerateLOD(chunkSize, 1, true, false, false, true); + LOD1TrimNorthWest = GenerateLOD(chunkSize, 1, true, false, true, false); + LOD1TrimSouthEast = GenerateLOD(chunkSize, 1, false, true, false, true); + LOD1TrimSouthWest = GenerateLOD(chunkSize, 1, false, true, true, false); + //------------------------------------LOD_2------------------- + LOD2Full = GenerateLOD(chunkSize, 2, false, false, false, false); + LOD2TrimAllSides = GenerateLOD(chunkSize, 2, true, true, true, true); + LOD2TrimNorth = GenerateLOD(chunkSize, 2, true, false, false, false); + LOD2TrimSouth = GenerateLOD(chunkSize, 2, false, true, false, false); + LOD2TrimWest = GenerateLOD(chunkSize, 2, false, false, true, false); + LOD2TrimEast = GenerateLOD(chunkSize, 2, false, false, false, true); + LOD2TrimNorthEast = GenerateLOD(chunkSize, 2, true, false, false, true); + LOD2TrimNorthWest = GenerateLOD(chunkSize, 2, true, false, true, false); + LOD2TrimSouthEast = GenerateLOD(chunkSize, 2, false, true, false, true); + LOD2TrimSouthWest = GenerateLOD(chunkSize, 2, false, true, true, false); + + //------------------------------------LOD_3------------------- + LOD3Full = GenerateLOD(chunkSize, 3, false, false, false, false); + LOD3TrimAllSides = GenerateLOD(chunkSize, 3, true, true, true, true); + LOD3TrimNorth = GenerateLOD(chunkSize, 3, true, false, false, false); + LOD3TrimSouth = GenerateLOD(chunkSize, 3, false, true, false, false); + LOD3TrimWest = GenerateLOD(chunkSize, 3, false, false, true, false); + LOD3TrimEast = GenerateLOD(chunkSize, 3, false, false, false, true); + LOD3TrimNorthEast = GenerateLOD(chunkSize, 3, true, false, false, true); + LOD3TrimNorthWest = GenerateLOD(chunkSize, 3, true, false, true, false); + LOD3TrimSouthEast = GenerateLOD(chunkSize, 3, false, true, false, true); + LOD3TrimSouthWest = GenerateLOD(chunkSize, 3, false, true, true, false); + //------------------------------------LOD_4------------------- + LOD4Full = GenerateLOD(chunkSize, 4, false, false, false, false); + + LOD lod1; + lod1.allSides = LOD1TrimAllSides; + + LOD lod2; + lod2.Top = LOD2TrimNorth; + lod2.Bottom = LOD2TrimSouth; + lod2.Left = LOD2TrimWest; + lod2.Right = LOD2TrimEast; + lod2.TopLeft = LOD2TrimNorthWest; + lod2.TopRight = LOD2TrimNorthEast; + lod2.BottomLeft = LOD2TrimSouthWest; + lod2.BottomRight = LOD2TrimSouthEast; + lod2.full = LOD2Full; + lod2.allSides = LOD2TrimAllSides; + + LOD lod3; + lod3.Top = LOD3TrimNorth; + lod3.Bottom = LOD3TrimSouth; + lod3.Left = LOD3TrimWest; + lod3.Right = LOD3TrimEast; + lod3.TopLeft = LOD3TrimNorthWest; + lod3.TopRight = LOD3TrimNorthEast; + lod3.BottomLeft = LOD3TrimSouthWest; + lod3.BottomRight = LOD3TrimSouthEast; + lod3.full = LOD3Full; + lod3.allSides = LOD3TrimAllSides; + + LOD lod4; + lod4.full = LOD4Full; + + lods.push_back(lod1); + lods.push_back(lod2); + lods.push_back(lod3); + lods.push_back(lod4); + + solidState = CG::me()->GetRenderer()->CreateRasterizerState(FILL_SOLID); + wireState = CG::me()->GetRenderer()->CreateRasterizerState(FILL_WIREFRAME); + + vertexCount = (TerrainWidth + 1) * (TerrainWidth + 1); + vb = CG::me()->GetRenderer()->CreateVertexBufferDynamic(vertexCount * sizeof(TerraVertex), sizeof(TerraVertex)); + + float multiplier = (float)(0xffff/MAX_HEIGHT); + + heightMap = new USHORT[(TerrainWidth + 1) * (TerrainWidth + 1)]; + + //load height map + for(int y = 0; y <= TerrainWidth; y++) + { + for(int x = 0; x <= TerrainWidth; x++) + { + UINT col = sampleTex(data, info, (float)x / (TerrainWidth + 2), 1.0f - ((float)y / (TerrainWidth + 2))); + + USHORT h = (USHORT)(col & 0xff) * (0xffff / 0xff); + int index = y * (TerrainWidth + 1) + x; + heightMap[index] = h; + } + } + + //smooth terrain heights + int numSmoothSteps = 1; + + for(int i = 0; i < numSmoothSteps; i++) + { + //smooth by X + for(int y = 0; y <= TerrainWidth; y++) + { + for(int x = 1; x < TerrainWidth; x++) + { + int idL = y * (TerrainWidth + 1) + (x - 1); + int idC = y * (TerrainWidth + 1) + (x); + int idR = y * (TerrainWidth + 1) + (x + 1); + float h1 = (float)heightMap[idL]; + float h2 = (float)heightMap[idC]; + float h3 = (float)heightMap[idR]; + + USHORT avg = (USHORT)((h1 + h2 + h3) / 3.0f); + + heightMap[idL] = avg; + heightMap[idC] = avg; + heightMap[idR] = avg; + } + } + + //smooth by Y + for(int y = 1; y < TerrainWidth; y++) + { + for(int x = 0; x <= TerrainWidth; x++) + { + int idT = (y - 1) * (TerrainWidth + 1) + x; + int idC = y * (TerrainWidth + 1) + x; + int idB = (y + 1) * (TerrainWidth + 1) + x; + float h1 = (float)heightMap[idT]; + float h2 = (float)heightMap[idC]; + float h3 = (float)heightMap[idB]; + + USHORT avg = (USHORT)((h1 + h2 + h3) / 3.0f); + + heightMap[idT] = avg; + heightMap[idC] = avg; + heightMap[idB] = avg; + } + } + } + + //build terrain geometry + TerraVertex* vertexData = new TerraVertex[vertexCount]; + for(int y = 0; y <= TerrainWidth; y++) + { + for(int x = 0; x <= TerrainWidth; x++) + { + int index = y * (TerrainWidth + 1) + x; + vertexData[index] = TerraVertex(index, 0,0, heightMap[index]); + } + } + + delete info; + delete[] data; + + //compute normals + LifeMath::float3 p1, p2, p3, d1, d2, n; + TerraVertex v1, v2, v3; + + for(int y = 0; y < TerrainWidth; y++) + { + for(int x = 0; x < TerrainWidth; x++) + { + //first triangle + int i1 = y * (TerrainWidth + 1) + x; + int i2 = y * (TerrainWidth + 1) + x + 1; + int i3 = (y + 1) * (TerrainWidth + 1) + x; + + v1 = vertexData[i1]; + v2 = vertexData[i2]; + v3 = vertexData[i3]; + + p1 = LifeMath::float3((float)(x), (float)v1.height / multiplier,-(float)(y)); + p2 = LifeMath::float3((float)(x + 1), v2.height / multiplier, -(float)(y)); + p3 = LifeMath::float3((float)(x), (float)v3.height / multiplier, -(float)(y + 1)); + + d1 = p2 - p1; + d2 = p3 - p1; + + n = LifeMath::Vec3Cross(d1,d2); + LifeMath::Vec3Normalize(&n); + + printf("ACHTUNG! Maybe wrong value computed for normal! What about NEAGTIVE numbers?\n"); + char nnx = (char)(n.X * 127.0f); + char nnz = (char)(n.Z * 127.0f); + + vertexData[i1].nX = nnx; + vertexData[i1].nZ = nnz; + + vertexData[i2].nX = nnx; + vertexData[i2].nZ = nnz; + + vertexData[i3].nX = nnx; + vertexData[i3].nZ = nnz; + + //second triangle + i1 = (y + 1) * (TerrainWidth + 1) + x; + i2 = y * (TerrainWidth + 1) + x + 1; + i3 = (y + 1) * (TerrainWidth + 1) + x + 1; + + v1 = vertexData[i1]; + v2 = vertexData[i2]; + v3 = vertexData[i3]; + + p1 = LifeMath::float3((float)(x), v1.height / multiplier, -(float)(y + 1)); + p2 = LifeMath::float3((float)(x + 1), v2.height / multiplier, -(float)(y)); + p3 = LifeMath::float3((float)(x + 1), v3.height / multiplier, -(float)(y + 1)); + + d1 = p2 - p1; + d2 = p3 - p1; + + n = LifeMath::Vec3Cross(d1,d2); + LifeMath::Vec3Normalize(&n); + + nnx = (char)(n.X * 127.0f); + nnz = (char)(n.Z * 127.0f); + + vertexData[i1].nX = nnx; + vertexData[i1].nZ = nnz; + + vertexData[i2].nX = nnx; + vertexData[i2].nZ = nnz; + + vertexData[i3].nX = nnx; + vertexData[i3].nZ = nnz; + } + } + + LifeCore::IDataStream* stm = vb->Map(0,LF_Discard); + stm->Write(vertexData, vertexCount * sizeof(TerraVertex)); + vb->Unmap(); + + int chunkStride = (TerrainWidth / chunkSize); + + //create and process chunk info + chunks = new TerraChunk[chunkStride * chunkStride]; + for(int x = 0; x < chunkStride; x++) + { + for(int y = 0; y < chunkStride; y++) + { + int index = y * chunkStride + x; + chunks[index].Offset = y * (TerrainWidth + 1) * chunkSize + x * chunkSize; + chunks[index].Column = x; + chunks[index].Row = y; + chunks[index].LOD = 1; + chunks[index].IB = LOD1Full; + + //now compute chunk bounding box + int minX = x * ChunkWidth; + int maxX = minX + ChunkWidth; + + int minY = y * ChunkWidth; + int maxY = minY + ChunkWidth; + + UINT minH; + UINT maxH; + + minH = maxH = vertexData[minY * (TerrainWidth + 1) + minX].height; + + for (int i = minX; i<=maxX; i++) + { + for (int j = minY; j<=maxY; j++) + { + UINT h = vertexData[j * (TerrainWidth + 1) + i].height; + if(minH > h) + minH = h; + if(maxH < h) + maxH = h; + } + } + + chunks[index].BoundingBox.resize(LifeMath::float3((float)(x * ChunkWidth), (float)minH / multiplier, -(float)(maxY)), + LifeMath::float3((float)(maxX), (float)maxH / multiplier, -(float)(minY))); + + /*printf("Chunk= X:%i Y:%i\nMin= X:%f Z:%f\nMax= X:%f Z:%f\n\n", x,y,chunks[index].BoundingBox.minimum().X, + chunks[index].BoundingBox.minimum().Z, chunks[index].BoundingBox.maximum().X, + chunks[index].BoundingBox.maximum().Z );*/ + } + } + + delete[] vertexData; + + IInputElement elements[] = + { + IInputElement("POSITION", 0, 0, FORMAT_R32_UINT, 0, 0, 0), + IInputElement("TEXCOORD", 0, 4, FORMAT_R8G8_SINT, 0, 0, 0), + IInputElement("TEXCOORD", 1, 6, FORMAT_R16_UINT, 0, 0, 0), + }; + + layout = CG::me()->GetRenderer()->CreateInputLayout(elements, 3, terraVs); + } + ~Terrain() + { + delete[] chunks; + SAFE_DELETE(vb); + SAFE_DELETE(layout); + SAFE_DELETE(terraVs); + SAFE_DELETE(terraPs_g); + diffuse->Release(); + + SAFE_DELETE(solidState); + SAFE_DELETE(wireState); + SAFE_DELETE_ARRAY(heightMap); + + SAFE_DELETE(LOD1Full); + SAFE_DELETE(LOD1TrimAllSides); + SAFE_DELETE(LOD1TrimNorthEast); + SAFE_DELETE(LOD1TrimNorthWest); + SAFE_DELETE(LOD1TrimSouthEast); + SAFE_DELETE(LOD1TrimSouthWest); + + SAFE_DELETE(LOD2Full); + SAFE_DELETE(LOD2TrimAllSides); + SAFE_DELETE(LOD2TrimNorth); + SAFE_DELETE(LOD2TrimSouth); + SAFE_DELETE(LOD2TrimEast); + SAFE_DELETE(LOD2TrimWest); + SAFE_DELETE(LOD2TrimNorthEast); + SAFE_DELETE(LOD2TrimNorthWest); + SAFE_DELETE(LOD2TrimSouthEast); + SAFE_DELETE(LOD2TrimSouthWest); + + SAFE_DELETE(LOD3Full); + SAFE_DELETE(LOD3TrimAllSides); + SAFE_DELETE(LOD3TrimNorth); + SAFE_DELETE(LOD3TrimSouth); + SAFE_DELETE(LOD3TrimEast); + SAFE_DELETE(LOD3TrimWest); + SAFE_DELETE(LOD3TrimNorthEast); + SAFE_DELETE(LOD3TrimNorthWest); + SAFE_DELETE(LOD3TrimSouthEast); + SAFE_DELETE(LOD3TrimSouthWest); + + SAFE_DELETE(LOD4Full); + } + void Update(Camera* cam, const LifeMath::float3& sunDir) + { + TERRA_DATA newData; + newData.size = (float)TerrainWidth; + newData.sunDir = sunDir; + terraCb->Update(&newData, sizeof(TERRA_DATA)); + + visibleChunks.clear(); + + LifeMath::float3 pos = cam->getPosition(); + int pX = static_cast(floorf(pos.X)); + int pZ = static_cast(floorf(pos.Z)); + + int col = pX / ChunkWidth; + int row = -pZ / ChunkWidth; + + //int vis = 0; + + //update all terrain chunks to minimal LOD (FIX IT: should not update ALL terrain) + int chunkStride = TerrainWidth / ChunkWidth; + for(int x = 0; x < chunkStride; x++) + { + for(int y = 0; y < chunkStride; y++) + { + if(cam->getFrustum()->BoxInFrustum(chunks[y * chunkStride + x].BoundingBox)) + { + visibleChunks.push_back(&chunks[y * chunkStride + x]); + } + } + } + + //check if cam is in terrain area + if(col >= 0 && row >= 0 && col < (TerrainWidth / ChunkWidth) && row < (TerrainWidth / ChunkWidth)) + { + int numChunks = visibleChunks.size(); + for(int i = 0; i < numChunks; i++) + { + TerraChunk* c = visibleChunks[i]; + int diffCol = col - c->Column; + int diffRow = row - c->Row; + + int lodNum = abs(diffCol) > abs(diffRow) ? abs(diffCol) : abs(diffRow); + + if(lodNum < 3) + { + switch(lodNum) + { + case 0: + c->LOD = 1; + c->IB = LOD1TrimAllSides; + break; + default: + LOD* curLod = &lods[lodNum]; + c->LOD = lodNum + 1; + if(abs(diffRow) > abs(diffCol)) + { + if(diffRow > 0) + c->IB = curLod->Left; + else + c->IB = curLod->Right; + } + else if(abs(diffRow) < abs(diffCol)) + { + if(diffCol > 0) + c->IB = curLod->Top; + else + c->IB = curLod->Bottom; + } + else + { + if(diffCol < 0 && diffRow < 0) + c->IB = curLod->BottomRight; + else if(diffCol > 0 && diffRow < 0) + c->IB = curLod->TopRight; + else if(diffCol < 0 && diffRow > 0) + c->IB = curLod->BottomLeft; + else + c->IB = curLod->TopLeft; + } + + break; + } + } + else + { + c->LOD = 4; + c->IB = LOD4Full; + } + } + } + // printf("Visible chunks: %i\n", visibleChunks.size()); + } + void Render(PassType pass) + { + IRenderer* renderer = CG::me()->GetRenderer(); + + renderer->SetPSConstantBuffer(terraCb, 2); + //renderer->SetRasterizerState(wireState); + renderer->SetPrimitiveTopology(PT_TRIANGLE_LIST); + renderer->SetVertexBuffer(vb); + if(pass == PT_COMPOSE_G_BUF) + { + renderer->SetPixelShader(terraPs_g); + renderer->SetVertexShader(terraVs_g); + } + else if(pass == PT_RENDER_SHADOW_MAP) + { + renderer->SetVertexShader(terraVs); + } + else + { + renderer->SetPixelShader(terraPs); + renderer->SetVertexShader(terraVs); + } + + renderer->SetInputLayout(layout); + renderer->SetPSTexture(diffuse.ptr(), 0); + + int chunkStride = TerrainWidth / ChunkWidth; + + int renderCount = visibleChunks.size(); + + for (int j = 0; j < renderCount; j++) + { + TerraChunk* c = visibleChunks[j]; + //CG.GraphicsDevice.VertexFormat = g_terra.TerrainGeometryBuffer.Description.FVF; + //if (c->Visible) + //{ + switch (c->LOD) + { + case 1: + renderer->SetIndexBuffer(c->IB); + //renderer->DrawIndexed(chunks[index].Offset, (ChunkWidth / 1) * (ChunkWidth / 1) * 2); + renderer->DrawIndexed(0, (ChunkWidth / 1) * (ChunkWidth / 1) * 2, c->Offset); + break; + case 2: + renderer->SetIndexBuffer(c->IB); + renderer->DrawIndexed(0, (ChunkWidth / 2) * (ChunkWidth / 2) * 2, c->Offset); + break; + case 3: + renderer->SetIndexBuffer(c->IB); + renderer->DrawIndexed(0, (ChunkWidth / 4) * (ChunkWidth / 4) * 2, c->Offset); + break; + case 4: + renderer->SetIndexBuffer(c->IB); + renderer->DrawIndexed(0, (ChunkWidth / 8) * (ChunkWidth / 8) * 2, c->Offset); + break; + } + //} + } + //renderer->SetRasterizerState(solidState); + } + float GetHeight(float x, float z) const + { + //return 0.0f; + /* + UINT col = sampleTex(data, info, (float)x / (TerrainWidth + 2), 1.0f - ((float)y / (TerrainWidth + 2))); + float r = (float)(col & 0xff) / 255.0f * AS_FLOAT(MAX_HEIGHT); + float g = (float)((col & 0xff00) >> 8) / 255.0f * AS_FLOAT(MAX_HEIGHT); + float b = (float)((col & 0xff0000) >> 16) / 255.0f * AS_FLOAT(MAX_HEIGHT); + + float h = (r + g + b) / 3.0f * multiplier;*/ + + float multiplier = (float)(0xffff/MAX_HEIGHT); + + z = -z; + + if(x < 0 || x > TerrainWidth || z < 0 || z > TerrainWidth) return 0; + + int iX = (int)x; + int iZ = (int)z; + + float fX = x - (float)iX; + float fZ = z - (float)iZ; + + float y1 = ((float)heightMap[(iZ + 0) * (TerrainWidth + 1) + (iX + 0)]) / multiplier; + float y2 = ((float)heightMap[(iZ + 0) * (TerrainWidth + 1) + (iX + 1)]) / multiplier; + float y3 = ((float)heightMap[(iZ + 1) * (TerrainWidth + 1) + (iX + 1)]) / multiplier; + float y4 = ((float)heightMap[(iZ + 1) * (TerrainWidth + 1) + (iX + 0)]) / multiplier; + + if((1.0f - fZ) >= (fX)) + return y1 + (y2 - y1) * fX + (y4 - y1) * fZ; //top triangle + else + return y3 + (y4 - y3) * (1.0f - fX) + (y2 - y3) * (1.0f - fZ); //bottom triangle + } +private: + + IIndexBuffer* LOD1Full; + IIndexBuffer* LOD1TrimAllSides; + IIndexBuffer* LOD1TrimNorthEast; + IIndexBuffer* LOD1TrimNorthWest; + IIndexBuffer* LOD1TrimSouthEast; + IIndexBuffer* LOD1TrimSouthWest; + + IIndexBuffer* LOD2Full; + IIndexBuffer* LOD2TrimAllSides; + IIndexBuffer* LOD2TrimNorth; + IIndexBuffer* LOD2TrimSouth; + IIndexBuffer* LOD2TrimEast; + IIndexBuffer* LOD2TrimWest; + IIndexBuffer* LOD2TrimNorthEast; + IIndexBuffer* LOD2TrimNorthWest; + IIndexBuffer* LOD2TrimSouthEast; + IIndexBuffer* LOD2TrimSouthWest; + + IIndexBuffer* LOD3Full; + IIndexBuffer* LOD3TrimAllSides; + IIndexBuffer* LOD3TrimNorth; + IIndexBuffer* LOD3TrimSouth; + IIndexBuffer* LOD3TrimEast; + IIndexBuffer* LOD3TrimWest; + IIndexBuffer* LOD3TrimNorthEast; + IIndexBuffer* LOD3TrimNorthWest; + IIndexBuffer* LOD3TrimSouthEast; + IIndexBuffer* LOD3TrimSouthWest; + + IIndexBuffer* LOD4Full; + + int ChunkWidth; + int visibleCunkOffset; + + IIndexBuffer* GenerateLOD(uint32 chunkLength, uint32 lodNumber, bool trimTop, bool trimBottom, bool trimLeft, bool trimRight) + { + uint32 mNum = 1, Counter = 0; + switch (lodNumber) + { + case 1: + mNum = 1; + break; + case 2: + mNum = 2; + break; + case 3: + mNum = 4; + break; + case 4: + mNum = 8; + break; + } + + unsigned int* Indices = new unsigned int[(chunkLength / mNum) * (chunkLength / mNum) * 6]; + for (uint32 i = 0; i < chunkLength / mNum; i++) + { + for (uint32 j = 0; j < chunkLength / mNum; j++) + { + Indices[Counter] = (i + 1) * (TerrainWidth + 1) * mNum + j * mNum; + Counter += 1; + Indices[Counter] = i * (TerrainWidth + 1) * mNum + j * mNum; + Counter += 1; + + Indices[Counter] = i * (TerrainWidth + 1) * mNum + (j + 1) * mNum; + Counter += 1; + + Indices[Counter] = (i + 1) * (TerrainWidth + 1) * mNum + j * mNum; + Counter += 1; + Indices[Counter] = i * (TerrainWidth + 1) * mNum + (j + 1) * mNum; + Counter += 1; + + Indices[Counter] = (i + 1) * (TerrainWidth + 1) * mNum + (j + 1) * mNum; + Counter += 1; + } + } + + //-----------------LOD trimmers---------------------------- + + //trim top corner + if (trimTop) + { + for (unsigned int i = 0; i < (chunkLength / mNum); i++) + { + if ((int)((float)((i + 1) / 2.0f)) == (float)((i + 1) / 2.0f)) + { + //÷¸òíàÿ + Indices[i * chunkLength / mNum * 6 + 0] = 0; + Indices[i * chunkLength / mNum * 6 + 1] = 0; + Indices[i * chunkLength / mNum * 6 + 2] = 0; + } + else + { + Indices[i * chunkLength / mNum * 6 + 3] = Indices[(i + 1) * chunkLength / mNum * 6 + 3]; + if (trimLeft == true) + { + if (i != 0) + Indices[i * chunkLength / mNum * 6 + 4] -= (mNum); + else + Indices[i * chunkLength / mNum * 6 + 0] -= (mNum); + } + else + { + Indices[i * chunkLength / mNum * 6 + 4] -= (mNum); + } + Indices[i * chunkLength / mNum * 6 + 0] += (mNum); + } + } + } + + //trim bottom corner + if (trimBottom) + { + for (unsigned int i = 0; i < (chunkLength / mNum); i++) + { + if ((int)((float)((i + 1) / 2.0f)) == (float)((i + 1) / 2.0f)) + { + //÷¸òíàÿ + Indices[(i + 1) * (chunkLength / mNum) * 6 - 6 + 0] = 0; + Indices[(i + 1) * (chunkLength / mNum) * 6 - 6 + 1] = 0; + Indices[(i + 1) * (chunkLength / mNum) * 6 - 6 + 2] = 0; + Indices[(i + 1) * (chunkLength / mNum) * 6 - 6 + 4] -= mNum; + } + else + { + Indices[(i + 1) * (chunkLength / mNum) * 6 - 6 + 5] = Indices[(i + 2) * (chunkLength / mNum) * 6 - 6 + 5]; + } + } + } + int offset = (chunkLength / mNum) * ((chunkLength / mNum) - 1) * 6; + if (trimRight) + { + //Trim right corner + + for (unsigned int i = 1; i < (chunkLength / mNum) + 1; i++) + { + if ((int)((float)i / 2.0f) == (float)i / 2.0f) + { + //÷¸òíàÿ + Indices[offset + (i - 1) * 6 + 0] += (mNum); + Indices[offset + (i - 1) * 6 + 3] = 0; + Indices[offset + (i - 1) * 6 + 4] = 0; + Indices[offset + (i - 1) * 6 + 5] = 0; + } + else + { + Indices[offset + (i - 1) * 6 + 5] += (mNum); + } + } + } + if (trimLeft) + { + //Trim left corner + offset = (chunkLength / mNum) * 6; + for (unsigned int i = 1; i < (chunkLength / mNum) + 1; i++) + { + if ((int)((float)i / 2.0f) == (float)i / 2.0f) + { + Indices[(i - 1) * 6 + 0] = 0; + Indices[(i - 1) * 6 + 1] = 0; + Indices[(i - 1) * 6 + 2] = 0; + } + else + { + Indices[(i - 1) * 6 + 0] += (mNum); + Indices[(i - 1) * 6 + 2] += (mNum); + Indices[(i - 1) * 6 + 4] -= (mNum); + } + } + } + + IIndexBuffer* _ib = CG::me()->GetRenderer()->CreateIndexBuffer((chunkLength / mNum) * (chunkLength / mNum) * 6 * sizeof(unsigned int),Indices, FALSE); + return _ib; + } + + static void GenerateMaxLOD(int chunkLength, int* indices) + { + // int[] indices = new int[chunkLength * chunkLength * 6]; + int counter = 0; + for (int i = 0; i < chunkLength; i++) + { + for (int j = 0; j < chunkLength; j++) + { + //First cell's triangle + indices[counter] = i * (chunkLength + 1) + (j + 1); + counter += 1; + indices[counter] = (i + 1) * (chunkLength + 1) + j; + counter += 1; + indices[counter] = i * (chunkLength + 1) + j; + counter += 1; + + //Second cell's triangle + indices[counter] = (i + 1) * (chunkLength + 1) + (j + 1); + counter += 1; + indices[counter] = (i + 1) * (chunkLength + 1) + j; + counter += 1; + indices[counter] = i * (chunkLength + 1) + (j + 1); + counter += 1; + } + } + } +}; +} + + +#endif // Terrain_h__ \ No newline at end of file diff --git a/LifeGraphics/TypesVideo.h b/LifeGraphics/TypesVideo.h new file mode 100644 index 0000000..34fc595 --- /dev/null +++ b/LifeGraphics/TypesVideo.h @@ -0,0 +1,12 @@ +#ifndef TypesVideo_h__ +#define TypesVideo_h__ + +#include "resource_ptr.h" +#include "render/ITexture2D.h" +#include "render/ITextureCube.h" + +namespace LifeGraphics +{ + typedef resource_ptr texture_ptr; +} +#endif // TypesVideo_h__ \ No newline at end of file diff --git a/LifeGraphics/Vertex.h b/LifeGraphics/Vertex.h new file mode 100644 index 0000000..23dc9b9 --- /dev/null +++ b/LifeGraphics/Vertex.h @@ -0,0 +1,64 @@ +#include "math/LifeMath.h" + +namespace LifeGraphics +{ + enum VERTEX_FORMAT + { + VF_POSITION = 0, + VF_TEXCOORD = 1 << 0 + }; + namespace Vertex + { + struct SVertex + { + LifeMath::float3 Position; + LifeMath::float2 Texcoord; + LifeMath::float3 Normal; + static int Stride(){ return sizeof(LifeMath::float3) * 2 + sizeof(LifeMath::float2); } + }; + + struct SVertexTbn + { + LifeMath::float3 Position; + LifeMath::float2 Texcoord; + LifeMath::float3 Normal; + LifeMath::float3 Tangent; + LifeMath::float3 Binormal; + static int Stride(){ return sizeof(LifeMath::float3) * 4 + sizeof(LifeMath::float2); } + }; + + struct SVertexTbnSkin + { + LifeMath::float3 Position; + LifeMath::float2 Texcoord; + LifeMath::float3 Normal; + LifeMath::float3 Tangent; + LifeMath::float3 Binormal; + LifeMath::float4 BlendIndices; + LifeMath::float4 BlendWeights; + static int Stride(){ return sizeof(LifeMath::float3) * 4 + sizeof(LifeMath::float2) + sizeof(LifeMath::float4) * 2; } + }; + + /*if(header.expSkin) + { + numElements = 7; + elements = new IInputElement[numElements]; + elements[0] = IInputElement("POSITION", 0, 0, FORMAT_R32G32B32_FLOAT, 0, 0, 0); + elements[1] = IInputElement("TEXCOORD", 0, 12, FORMAT_R32G32_FLOAT, 0, 0, 0); + elements[2] = IInputElement("NORMAL", 0, 20, FORMAT_R32G32B32_FLOAT, 0, 0, 0); + elements[3] = IInputElement("TANGENT", 0, 32, FORMAT_R32G32B32_FLOAT, 0, 0, 0); + elements[4] = IInputElement("BINORMAL", 0, 44, FORMAT_R32G32B32_FLOAT, 0, 0, 0); + elements[5] = IInputElement("BLENDINDICES", 0, 56, FORMAT_R32G32B32A32_FLOAT, 0, 0, 0); + elements[6] = IInputElement("BLENDWEIGHT", 0, 72, FORMAT_R32G32B32A32_FLOAT, 0, 0, 0); + } + else + { + elements = new IInputElement[numElements]; + elements[0] = IInputElement("POSITION", 0, 0, FORMAT_R32G32B32_FLOAT, 0, 0, 0); + elements[1] = IInputElement("TEXCOORD", 0, 12, FORMAT_R32G32_FLOAT, 0, 0, 0); + elements[2] = IInputElement("NORMAL", 0, 20, FORMAT_R32G32B32_FLOAT, 0, 0, 0); + elements[3] = IInputElement("TANGENT", 0, 32, FORMAT_R32G32B32_FLOAT, 0, 0, 0); + elements[4] = IInputElement("BINORMAL", 0, 44, FORMAT_R32G32B32_FLOAT, 0, 0, 0); + }*/ + } +} \ No newline at end of file diff --git a/LifeGraphics/XML.h b/LifeGraphics/XML.h new file mode 100644 index 0000000..819137f --- /dev/null +++ b/LifeGraphics/XML.h @@ -0,0 +1,22 @@ +#ifndef XML_h__ +#define XML_h__ + +#include "pugixml.hpp" + +class LifeXMLWriter : public pugi::xml_writer +{ +public: + std::string result; + int length; + LifeXMLWriter() + :length(0) + { + } + + virtual void write(const void* data, size_t size) + { + result += std::string(static_cast(data), size); + length += size; + } +}; +#endif // XML_h__ \ No newline at end of file diff --git a/LifeGraphics/camera/Camera.h b/LifeGraphics/camera/Camera.h new file mode 100644 index 0000000..ddb5b2b --- /dev/null +++ b/LifeGraphics/camera/Camera.h @@ -0,0 +1,329 @@ +#ifndef Camera_h__ +#define Camera_h__ + +#include "IGraphicsNode.h" +#include "math/LifeMath.h" +#include "camera/Frustum.h" + +namespace LifeGraphics +{ + class Camera : public IGraphicsNode + { + private: + bool isViewValid; + bool isProjValid; + float fov; + float nearPlane; + float farPlane; + float viewAspect; + LifeMath::float3 right; + LifeMath::float3 eye; + LifeMath::float3 at; + LifeMath::float3 up; + float yaw, pitch, roll; + Frustum frustum; + LifeMath::float3 ViewDirection; + LifeMath::float4x4 viewMatrix; + LifeMath::float4x4 projectionMatrix; + bool freeCam; + LifeMath::float3 fDir; + NodeType getType() + { + return NT_CAMERA; + } + void UpdatePerspectiveMatrix() + { + Mat4x4PerspectiveFOV(fov, viewAspect, nearPlane, farPlane, projectionMatrix); + isProjValid = true; + frustum.UpdateFrustum(viewMatrix * projectionMatrix); + } + + void ComputeView() + { + //g_viewMatrix = Matrix4x4.LookAtLH(g_eye, g_at, g_up); + + ViewDirection = at - eye; + LifeMath::Vec3Normalize(&ViewDirection, &ViewDirection); + + up = LifeMath::float3(0,1,0); + + //fixup UP vector + if(LifeMath::Vec3Dot(&ViewDirection, &up) > 0.998f) + { + up = LifeMath::float3(0,0,-1); + } + else if(LifeMath::Vec3Dot(&ViewDirection, &up) < -0.998f) + { + up = LifeMath::float3(0,0,1); + } + + //orthoganalize right vector + LifeMath::Vec3Cross(up, ViewDirection, right); + LifeMath::Vec3Normalize(&right); + + //orthoganalize up vector + LifeMath::Vec3Cross(ViewDirection, right, up); + LifeMath::Vec3Normalize(&up); + + //compute view matrix + float x = -LifeMath::Vec3Dot(&right, &eye); + float y = -LifeMath::Vec3Dot(&up, &eye); + float z = -LifeMath::Vec3Dot(&ViewDirection, &eye); + + viewMatrix.M11 = right.X; + viewMatrix.M12 = up.X; + viewMatrix.M13 = ViewDirection.X; + viewMatrix.M14 = 0; + + viewMatrix.M21 = right.Y; + viewMatrix.M22 = up.Y; + viewMatrix.M23 = ViewDirection.Y; + viewMatrix.M24 = 0; + + viewMatrix.M31 = right.Z; + viewMatrix.M32 = up.Z; + viewMatrix.M33 = ViewDirection.Z; + viewMatrix.M34 = 0; + + viewMatrix.M41 = x; + viewMatrix.M42 = y; + viewMatrix.M43 = z; + viewMatrix.M44 = 1.0f; + + isViewValid = true; + + frustum.UpdateFrustum(viewMatrix * projectionMatrix); + } + + public: + Camera(); + ~Camera(); + void update(float dt) + { + } + void setAspect(float aspect) + { + viewAspect = aspect; + isProjValid = false; + } + void setFov(float _fov) + { + fov = _fov; + isProjValid = false; + } + void setNear(float val) + { + nearPlane = val; + isProjValid = false; + } + void setFar(float val) + { + farPlane = val; + isProjValid = false; + } + float getNear() const + { + return nearPlane; + } + float getFar() const + { + return farPlane; + } + LifeMath::float4x4 getView() + { + if(isViewValid) + { + return viewMatrix; + } + else + { + ComputeView(); + isViewValid = true; + return viewMatrix; + } + } + LifeMath::float4x4 getProj() + { + if(isProjValid) + { + return projectionMatrix; + } + else + { + UpdatePerspectiveMatrix(); + isProjValid = true; + return projectionMatrix; + } + } + LifeMath::float3 getDirection() + { + return ViewDirection; + } + LifeMath::float3 getRight() + { + return right; + } + void setPosition(float x, float y, float z) + { + eye = LifeMath::float3(x, y, z); + isViewValid = false; + } + void setPosition(const LifeMath::float3& position) + { + eye = position; + isViewValid = false; + //ComputeView(); + /*isViewValid = false;*/ + } + void setDirection(LifeMath::float3& direction) + { + at = direction; + //at = direction - eye; + //LifeMath::Vec3Normalize(&at); + + up.X = 0.0f; + up.Y = 1.0f; + up.Z = 0.0f; + isViewValid = false; + //LifeMath::Vec3Cross(up, at, right); + //ComputeView(); + } + Frustum* getFrustum() + { + bool updateFrustum = false; + if(!isViewValid) + { + ComputeView(); + updateFrustum = true; + } + if(!isProjValid) + { + UpdatePerspectiveMatrix(); + updateFrustum = true; + } + + return &frustum; + } + const LifeMath::float3& getPosition() const + { + return eye; + } + + float getFov() const + { + return fov; + } + float getAspect() const + { + return viewAspect; + } + + const LifeMath::float3& getUp() const + { + return up; + } + + void Yaw(float angle) + { + yaw += angle; + LifeMath::float3 axis = LifeMath::float3(0.0f, 1.0f, 0.0f); + LifeMath::float4x4 r; + Mat4x4RotationAxis(&axis, angle, &r); + if (freeCam) + { + Vec3TransformCoordinate(right, r, right); + Vec3TransformCoordinate(at, r, at); + } + else + { + Vec3TransformCoordinate(fDir, r, fDir); + Vec3TransformCoordinate(right, r, right); + } + isViewValid = false; + } + + void Pitch(float angle) + { + pitch += angle; + LifeMath::float4x4 r; + Mat4x4RotationAxis(&right, angle, &r); + if (freeCam) + { + Vec3TransformCoordinate(up, r, up); + Vec3TransformCoordinate(at, r, at); + } + else + { + Vec3TransformCoordinate(up, r, up); + Vec3TransformCoordinate(fDir, r, fDir); + } + isViewValid = false; + } + + void Roll(float angle) + { + roll += angle; + LifeMath::float4x4 r; + Mat4x4RotationAxis(&at, angle, &r); + Vec3TransformCoordinate(up, r, up); + Vec3TransformCoordinate(right, r, at); + isViewValid = false; + } + void GetCorners(LifeMath::float3* corners) + { + if(!isViewValid) + ComputeView(); + if(!isProjValid) + UpdatePerspectiveMatrix(); + + LifeMath::float4x4 vp = viewMatrix * projectionMatrix; + Mat4x4Invert(&vp); + + for (int i = 0; i < 8; i++) + { + corners[i] = Vec3TransformCoordinate(vp, baseCorners[i]); + } + } + void SetupBasis(const LifeMath::float3& _pos, const LifeMath::float3& _front, const LifeMath::float3& _top, const LifeMath::float3& _right) + { + eye = _pos; + right = _right; + up = _top; + at = _front; + ViewDirection = at; + + LifeMath::Vec3Normalize(&right); + LifeMath::Vec3Normalize(&at); + LifeMath::Vec3Normalize(&up); + + //compute view matrix + float x = -LifeMath::Vec3Dot(&right, &eye); + float y = -LifeMath::Vec3Dot(&up, &eye); + float z = -LifeMath::Vec3Dot(&at, &eye); + + viewMatrix.M11 = right.X; + viewMatrix.M12 = up.X; + viewMatrix.M13 = at.X; + viewMatrix.M14 = 0.0f; + + viewMatrix.M21 = right.Y; + viewMatrix.M22 = up.Y; + viewMatrix.M23 = at.Y; + viewMatrix.M24 = 0.0f; + + viewMatrix.M31 = right.Z; + viewMatrix.M32 = up.Z; + viewMatrix.M33 = at.Z; + viewMatrix.M34 = 0.0f; + + viewMatrix.M41 = x; + viewMatrix.M42 = y; + viewMatrix.M43 = z; + viewMatrix.M44 = 1.0f; + + isViewValid = true; + } + }; +} + +#endif // Camera_h__ \ No newline at end of file diff --git a/LifeGraphics/camera/Frustum.h b/LifeGraphics/camera/Frustum.h new file mode 100644 index 0000000..cd2a0fc --- /dev/null +++ b/LifeGraphics/camera/Frustum.h @@ -0,0 +1,186 @@ +#ifndef Frustum_h__ +#define Frustum_h__ + +#include "math/LifeMath.h" +#include +#include "BoundingSphere.h" + +namespace LifeGraphics +{ + enum BoxIntersectionResult + { + Inside = 0, Outside = 1, Intersection = 2 + }; + + enum FrustumPlane + { + Top = 0, Bottom = 0, Left = 0, Right = 0, Near = 0, Far = 0 + }; + + static const LifeMath::float3 baseCorners[] = { + //front plane + LifeMath::float3(-1.0f, 1.0f, 1.0f), + LifeMath::float3(1.0f, 1.0f, 1.0f), + LifeMath::float3(1.0f, -1.0f, 1.0f), + LifeMath::float3(-1.0f, -1.0f, 1.0f), + + //rear plane + LifeMath::float3(-1.0f, 1.0f, -1.0f), + LifeMath::float3(1.0f, 1.0f, -1.0f), + LifeMath::float3(1.0f, -1.0f, -1.0f), + LifeMath::float3(-1.0f, -1.0f, -1.0f) + }; + + class Frustum + { + private: + + LifeMath::Plane frustrumPlanes[6]; + LifeMath::float3 corners[8]; + LifeMath::float4x4 vp; + + public: + Frustum() + { + for (int i = 0; i < 6; i++) + frustrumPlanes[i] = LifeMath::Plane(LifeMath::float3(0.0f, 0.0f, 0.0f), LifeMath::float3(0.0f, 1.0f, 0.0f)); + } + Frustum(LifeMath::Plane& _near, LifeMath::Plane& _far, LifeMath::Plane& _top, LifeMath::Plane& _bottom, + LifeMath::Plane& _left, LifeMath::Plane& _right) + { + frustrumPlanes[0] = _top; + frustrumPlanes[1] = _bottom; + frustrumPlanes[2] = _left; + frustrumPlanes[3] = _right; + frustrumPlanes[4] = _near; + frustrumPlanes[5] = _far; + } + Frustum(const LifeMath::float4x4& viewProjection) + { + UpdateFrustum(viewProjection); + } + ~Frustum(){} + void UpdateFrustum(const LifeMath::float4x4& viewProjection) + { + vp = viewProjection; + // Top plane + frustrumPlanes[0] = LifeMath::Plane( + viewProjection.M14 - viewProjection.M12, + viewProjection.M24 - viewProjection.M22, + viewProjection.M34 - viewProjection.M32, + viewProjection.M44 - viewProjection.M42); + + // Bottom plane + frustrumPlanes[1] = LifeMath::Plane( + viewProjection.M14 + viewProjection.M12, + viewProjection.M24 + viewProjection.M22, + viewProjection.M34 + viewProjection.M32, + viewProjection.M44 + viewProjection.M42); + + // Left plane + frustrumPlanes[2] = LifeMath::Plane( + viewProjection.M14 + viewProjection.M11, + viewProjection.M24 + viewProjection.M21, + viewProjection.M34 + viewProjection.M31, + viewProjection.M44 + viewProjection.M41); + + // Right plane + frustrumPlanes[3] = LifeMath::Plane( + viewProjection.M14 - viewProjection.M11, + viewProjection.M24 - viewProjection.M21, + viewProjection.M34 - viewProjection.M31, + viewProjection.M44 - viewProjection.M41); + + // Near plane + frustrumPlanes[4] = LifeMath::Plane( + viewProjection.M13, + viewProjection.M23, + viewProjection.M33, + viewProjection.M43); + + // Far plane + frustrumPlanes[5] = LifeMath::Plane( + viewProjection.M14 - viewProjection.M13, + viewProjection.M24 - viewProjection.M23, + viewProjection.M34 - viewProjection.M33, + viewProjection.M44 - viewProjection.M43); + + // Normalize planes + for (int i = 0; i < 6; i++) + { + LifeMath::PlaneNormalize(frustrumPlanes[i]); + } + } + + bool SphereInFrustum(const LifeMath::float3& center, float radius) const + { + LifeMath::float4 position = LifeMath::float4(center.X, center.Y, center.Z, 1.0f); + for (int i = 0; i < 6; i++) + if(LifeMath::PlaneDot(frustrumPlanes[i], position) + radius < 0) + return false; + return true; + } + + bool SphereInFrustum(const BoundingSphere* sphere) const + { + LifeMath::float4 position = LifeMath::float4(sphere->center.X, sphere->center.Y, sphere->center.Z, 1.0f); + for (int i = 0; i < 6; i++) + if(PlaneDot(frustrumPlanes[i], position) + sphere->radius < 0) + return false; + return true; + } + + bool BoxInFrustum(const LifeCore::BoundingBox3D& box) const + { + LifeMath::float3 corners[8]; + box.GetCorners(corners); + + for (int i = 0; i < 6; i++) + { + if (PlaneDot(frustrumPlanes[i], corners[0]) < 0 && + PlaneDot(frustrumPlanes[i], corners[1]) < 0 && + PlaneDot(frustrumPlanes[i], corners[2]) < 0 && + PlaneDot(frustrumPlanes[i], corners[3]) < 0 && + PlaneDot(frustrumPlanes[i], corners[4]) < 0 && + PlaneDot(frustrumPlanes[i], corners[5]) < 0 && + PlaneDot(frustrumPlanes[i], corners[6]) < 0 && + PlaneDot(frustrumPlanes[i], corners[7]) < 0) + { + return false; + } + } + return true; + } + + bool BoxInFrustum(const LifeMath::float3* corners) const + { + for (int i = 0; i < 6; i++) + { + if (PlaneDot(frustrumPlanes[i], corners[0]) < 0 && + PlaneDot(frustrumPlanes[i], corners[1]) < 0 && + PlaneDot(frustrumPlanes[i], corners[2]) < 0 && + PlaneDot(frustrumPlanes[i], corners[3]) < 0 && + PlaneDot(frustrumPlanes[i], corners[4]) < 0 && + PlaneDot(frustrumPlanes[i], corners[5]) < 0 && + PlaneDot(frustrumPlanes[i], corners[6]) < 0 && + PlaneDot(frustrumPlanes[i], corners[7]) < 0) + { + return false; + } + } + return true; + } + + const LifeMath::float3* GetCorners() + { + LifeMath::float4x4 ivp; + ivp = LifeMath::Mat4x4Invert(&vp); + for (int i = 0; i < 8; i++) + { + LifeMath::Vec3TransformCoordinate(baseCorners[i], ivp, corners[i]); + } + return corners; + } + }; +} +#endif // Frustum_h__ \ No newline at end of file diff --git a/LifeGraphics/constants.h b/LifeGraphics/constants.h new file mode 100644 index 0000000..cc24f5c --- /dev/null +++ b/LifeGraphics/constants.h @@ -0,0 +1,60 @@ +#ifndef constants_h__ +#define constants_h__ + +#include "math/LifeMath.h" + +struct PER_CAMERA_CONSTANTS +{ + LifeMath::float4x4 mView; + LifeMath::float4x4 mProj; + LifeMath::float4x4 mInvVP; + LifeMath::float3 camPos; + float d0; + LifeMath::float3 camDir; + float d1; + LifeMath::float4 camNearFar; + LifeMath::float3 corner3; + float corner3_dummy; + LifeMath::float3 corner2; + float corner2_dummy; + LifeMath::float3 corner1; + float corner1_dummy; + LifeMath::float3 corner0; + float corner0_dummy; +}; + +struct POINT_LIGHT_DATA +{ + LifeMath::float3 Position; + float Intensity; + LifeMath::float3 Color; + float Range; +}; + +struct DIR_LIGHT_DATA +{ + LifeMath::float3 Direction; + float Intensity; + LifeMath::float3 Color; + float SpecularPow; +}; + +struct SPOT_LIGHT_DATA +{ + LifeMath::float3 Direction; + float InnerAngle; + LifeMath::float3 Position; + float Intensity; + LifeMath::float3 Color; + float Range; + + float OuterAngle; + float ZNear; + float dummy2; + float dummy3; + LifeMath::float4x4 lightMatrix; + LifeMath::float4x4 lightView; + LifeMath::float4x4 meshMatrix; +}; + +#endif // constants_h__ \ No newline at end of file diff --git a/LifeGraphics/debug/IDebugDrawer.h b/LifeGraphics/debug/IDebugDrawer.h new file mode 100644 index 0000000..bf269fa --- /dev/null +++ b/LifeGraphics/debug/IDebugDrawer.h @@ -0,0 +1,231 @@ +#ifndef IDebugDrawer_h__ +#define IDebugDrawer_h__ + +#include "render/IRenderer.h" +#include "math/LifeMath.h" +#include + +namespace LifeGraphics +{ +#define MAX_VERTEX_COUNT 1024 + + struct DebugVertex + { + LifeMath::float3 position; + LifeMath::float3 color; + + const DebugVertex operator *(float val) const + { + DebugVertex result; + result.color = this->color * val; + result.position = this->position * val; + return result; + } + + const DebugVertex operator +(const DebugVertex& val) const + { + DebugVertex result; + result.color = this->color + val.color; + result.position = this->position + val.position; + return result; + } + + const DebugVertex& operator *=(float val) + { + this->color *= val; + this->position *= val; + return *this; + } + }; + + struct CB_2D_DATA + { + LifeMath::float4 screen_size; + }; + + class IDebugDrawer + { + private: + IShader* debugVS; + IShader* debugPS; + + IShader* debugVS_2d; + IShader* debugPS_2d; + + IInputLayout* inputLayout; + IVertexBuffer* debugVB; + DebugVertex geom[MAX_VERTEX_COUNT]; + int vertexCount; + + IConstantBuffer* cb_2d; + DebugVertex* buf; + public: + IDebugDrawer(IRenderer* _renderer) + :vertexCount(0) + { + //Vertex shader + String vs(STR( + "struct VS_INPUT" \ + "{" \ + " float4 Position : POSITION;" \ + " float3 Color : LineColor;" \ + "}; " \ + "struct VS_OUTPUT " \ + "{" \ + " float4 Position : SV_Position;" \ + " float3 Color : TEXCOORD0;" \ + "};" \ + "cbuffer perCameraData : register(b1)" \ + "{" \ + " row_major matrix matView;" \ + " row_major matrix matProjection;" \ + " row_major matrix matInvVP;" \ + " float4 camPos;" \ + " float4 camDir;" \ + " float4 camNearFar;" \ + " float4 corners[4];" \ + "};"\ + "VS_OUTPUT main_VS( VS_INPUT Input )" \ + "{"\ + " VS_OUTPUT Output = (VS_OUTPUT)0;" \ + " float4 pos = float4(Input.Position.xyz, 1.0f);" \ + " Output.Color = Input.Color;" \ + " Output.Position = mul(pos, matInvVP);" \ + " return( Output );" \ + "}" + )); + + //Pixel shader + String ps(STR( + "struct PS_INPUT " \ + "{" \ + " float4 Position : SV_Position;" \ + " float3 Color : TEXCOORD0;" \ + "};" \ + "float4 main_PS( PS_INPUT Input ) : SV_Target" \ + "{" \ + " return float4(Input.Color, 1.0f);" \ + "}" + )); + + String vs_line_2d(STR( + "cbuffer RT_Size: register(b1)" \ + "{float bbWidth : packoffset(c0.x);float bbHeight : packoffset(c0.y);};" \ + "struct VS_INPUT {float3 Position : POSITION0;float3 Color : LineColor0;};" \ + "struct VS_OUTPUT {float4 Position : SV_Position;float4 Color :TEXCOORD0;};" \ + "VS_OUTPUT main_VS( VS_INPUT Input ){" \ + "VS_OUTPUT Output;Output.Position = float4(Input.Position.xy, 0.0f, 1.0f);" \ + "Output.Position.xy = Output.Position.xy/float2(bbWidth, -bbHeight)*2.0f + float2(-1.0f,1.0f);" \ + "Output.Color = float4(Input.Color.xyz, 1.0f); return( Output );}" + )); + + String ps_2d_line(STR( + "struct PS_INPUT {" \ + "float4 Position : SV_Position;" \ + "float4 Color : TEXCOORD0;};" \ + "float4 main_PS( PS_INPUT Input ) : SV_Target" \ + "{ return Input.Color;}" + )); + + buf = new DebugVertex[2*256]; + + debugVS = ResourceManager::me()->CompileUserShaderVS(vs.c_str(), vs.length()); + debugPS = ResourceManager::me()->CompileUserShaderPS(ps.c_str(), ps.length()); + + debugVS_2d = ResourceManager::me()->CompileUserShaderVS(vs_line_2d.c_str(), vs_line_2d.length()); + debugPS_2d = ResourceManager::me()->CompileUserShaderPS(ps_2d_line.c_str(), ps_2d_line.length()); + + IInputElement* elements = new IInputElement[2]; + elements[0] = IInputElement("POSITION", 0, 0, FORMAT_R32G32B32_FLOAT, 0, 0, 0); + elements[1] = IInputElement("LineColor", 0, 12, FORMAT_R32G32B32_FLOAT, 0, 0, 0); + + inputLayout = CG::me()->GetRenderer()->CreateInputLayout(elements, 2, debugVS); + debugVB = CG::me()->GetRenderer()->CreateVertexBufferDynamic(sizeof(DebugVertex)* MAX_VERTEX_COUNT,sizeof(DebugVertex)); + + cb_2d = CG::me()->GetRenderer()->CreateConstantBuffer(sizeof(CB_2D_DATA)); + } + void DrawSpline2D(DebugVertex* points, int discr_rate) + { + int offset = 0; + DebugVertex prev; + prev = points[0]; + for(int i = 1; i <= discr_rate; i++) + { + //DebugVertex newP; + DebugVertex newP = LifeCore::CubicBezier((1.0f / (float)discr_rate) * (float)i, points[0], points[1], points[2], points[3]); + buf[offset++] = prev; + buf[offset++] = newP; + + prev = newP; + } + DrawLines2D(buf, discr_rate); + } + void DrawLines2D(DebugVertex* data, int line_count) + { + LifeCore::IDataStream* stm = debugVB->Map(0, LF_Discard | LF_NoOverwrite); + stm->Write(data, line_count * 2 * sizeof(DebugVertex)); + debugVB->Unmap(); + + CB_2D_DATA d; + d.screen_size = LifeMath::float4(640, 480,0,0); + cb_2d->Update(&d, sizeof(CB_2D_DATA)); + + IRenderer* r = CG::me()->GetRenderer(); + r->SetVSConstantBuffer(cb_2d, 1); + r->SetVertexBuffer(debugVB); + r->SetInputLayout(inputLayout); + r->SetPrimitiveTopology(PT_LINE_LIST); + r->SetVertexShader(debugVS_2d); + r->SetPixelShader(debugPS_2d); + r->Draw(line_count * 2, 0); + } + void Render() + { + if(vertexCount > 1) + { + LifeCore::IDataStream* stm = debugVB->Map(0, LF_Discard); + stm->Write(&geom[0], vertexCount * sizeof(DebugVertex)); + debugVB->Unmap(); + + IRenderer* r = CG::me()->GetRenderer(); + r->SetVertexBuffer(debugVB); + r->SetInputLayout(inputLayout); + r->SetPrimitiveTopology(PT_LINE_LIST); + r->SetVertexShader(debugVS); + r->SetPixelShader(debugPS); + r->Draw(vertexCount, 0); + } + } + void Flush() + { + vertexCount = 0; + } + /*void PushLine(const LifeMath::float3& v1, const LifeMath::float3& v2) + { + if(vertexCount < MAX_VERTEX_COUNT) + { + geom[vertexCount] = v1; + vertexCount++; + } + + if(vertexCount < MAX_VERTEX_COUNT) + { + geom[vertexCount] = v2; + vertexCount++; + } + }*/ + void PushLine(const DebugVertex& v1, const DebugVertex& v2) + { + //if(vertexCount < MAX_VERTEX_COUNT) + //{ + geom[vertexCount++] = v1; + //} + + //if(vertexCount < MAX_VERTEX_COUNT) + //{ + geom[vertexCount++] = v2; + //} + } + }; +} +#endif // IDebugDrawer_h__ \ No newline at end of file diff --git a/LifeGraphics/passPSSM.h b/LifeGraphics/passPSSM.h new file mode 100644 index 0000000..1729e41 --- /dev/null +++ b/LifeGraphics/passPSSM.h @@ -0,0 +1,455 @@ +#ifndef passPSSM_h__ +#define passPSSM_h__ + +#include "Globals.h" +#include "math/LifeMath.h" +#include "camera/Camera.h" +#include + +namespace LifeGraphics +{ + struct PSSM_CONST + { + LifeMath::float4x4 pssmMat[4]; + LifeMath::float4 splitRegions[4]; + float pssmSize; + LifeMath::float3 dummy_pssm; + }; + + class passPSSM + { + public: + IConstantBuffer* cbPssm; + IBlendState* shadowBlendState; + IBlendState* restoreBlendState; + LifeMath::float3 points[8]; + int splitCount; + float fSplitDistances[4]; + float fSplitSchemeLambda; + int depthSize; + LifeMath::float4x4 mLightView; + LifeMath::float4x4 mLightProj; + + LifeMath::float3 vLightSource; + LifeMath::float3 vLightTarget; + + float fLightNear; + float fLightFar; // dynamically adjusted + float fLightFarMax; + float fLightFOV; + + LifeMath::float4x4 lViews[4]; + LifeMath::float4x4 lProjs[4]; + + ITexture2D* depth; + + IShader* rtdShader; + Camera* sceneCam; + Frustum f; + Light* light; + LifeMath::Rect2i viewRegions[4]; + + float fNear; + float fFar; + + std::vector splitRegions; + float clampf(float val, float min, float max) + { + if(val < min) return min; + if(val > max) return max; + return val; + } + ~passPSSM() + { + if(cbPssm) + delete cbPssm; + if(depth) + delete depth; + if(rtdShader) + delete rtdShader; + } + passPSSM() + { + cbPssm = CG::me()->GetRenderer()->CreateConstantBuffer(sizeof(PSSM_CONST)); + fLightNear = 0.1f; + fLightFar = 350.0f; // dynamically adjusted + fLightFarMax = 350.0f; + fLightFOV = 3.14159265358979f / 2.0f; + splitCount = 3; + fSplitSchemeLambda = 0.92f; + depthSize = 2048; + + ComputeDepthRegionDistribution(); + } + + void ComputeDepthRegionDistribution() + { + splitRegions.clear(); + + LifeMath::float4 region0; + LifeMath::float4 region1; + LifeMath::float4 region2; + LifeMath::float4 region3; + + printf("FIX RECTANGLE!!!!\n"); + switch(splitCount) + { + case 1: + //----------| + //| | + //| 1 | + //| | + //| | + //----------| + region0 = LifeMath::float4(0.0f, 0.0f, 1.0f, 1.0f); + viewRegions[0] = LifeMath::Rect2i(0, 0, depthSize, depthSize); + splitRegions.push_back(region0); + break; + case 2: + //----------| + //| 1 | + //| | + //----------- + //| | + //| 2 | + //----------| + region0 = LifeMath::float4(0.0f, 0.0f, 1.0f, 0.5f); + viewRegions[0] = LifeMath::Rect2i(0, 0, depthSize, depthSize / 2); + + region1 = LifeMath::float4(0.0f, 0.5f, 1.0f, 0.5f); + viewRegions[1] = LifeMath::Rect2i(0, depthSize / 2, depthSize, depthSize / 2); + splitRegions.push_back(region0); + splitRegions.push_back(region1); + break; + case 3: + region0 = LifeMath::float4(0.0f, 0.0f, 1.0f, 0.5f); + viewRegions[0] = LifeMath::Rect2i(0, 0, depthSize, depthSize / 2); + + region1 = LifeMath::float4(0.0f, 0.5f, 0.5f, 0.5f); + viewRegions[1] = LifeMath::Rect2i(0, depthSize / 2, depthSize / 2, depthSize / 2); + + region2 = LifeMath::float4(0.5f, 0.5f, 0.5f, 0.5f); + viewRegions[2] = LifeMath::Rect2i(depthSize / 2, depthSize / 2, depthSize / 2, depthSize / 2); + + splitRegions.push_back(region0); + splitRegions.push_back(region1); + splitRegions.push_back(region2); + + break; + case 4: + + region0 = LifeMath::float4(0.0f, 0.0f, 0.5f, 0.5f); + viewRegions[0] = LifeMath::Rect2i(0, 0, depthSize / 2, depthSize / 2); + + region1 = LifeMath::float4(0.5f, 0.0f, 0.5f, 0.5f); + viewRegions[1] = LifeMath::Rect2i(depthSize / 2, 0, depthSize / 2, depthSize / 2); + + region2 = LifeMath::float4(0.0f, 0.5f, 0.5f, 0.5f); + viewRegions[2] = LifeMath::Rect2i(0, depthSize / 2, depthSize / 2, depthSize / 2); + + region3 = LifeMath::float4(0.5f, 0.5f, 0.5f, 0.5f); + viewRegions[3] = LifeMath::Rect2i(depthSize / 2, depthSize / 2, depthSize / 2, depthSize / 2); + + splitRegions.push_back(region0); + splitRegions.push_back(region1); + splitRegions.push_back(region2); + splitRegions.push_back(region3); + break; + } + } + + void Init() + { + //cam1 = new Camera(1, 1, ""); + + depth = CG::me()->GetRenderer()->CreateDepthTarget(depthSize, depthSize, FORMAT_R32_TYPELESS); + + rtdShader = ResourceManager::me()->CompileUserShaderPS(AS_STRING("RTD_PCF.psh")); + + shadowBlendState = CG::me()->GetRenderer()->CreateBlendState(false, false, BLEND_OP_ADD, BLEND_ZERO, BLEND_ONE, 0); + + restoreBlendState = CG::me()->GetRenderer()->CreateBlendState(true, false, BLEND_OP_ADD, BLEND_ONE, BLEND_ZERO, 1 | 2 | 4 | 8); + } + + void Begin(Camera* _sceneCam, Light* _light) + { + light = _light; + sceneCam = _sceneCam; + IRenderer* renderer = CG::me()->GetRenderer(); + + CalculateSplitPlanes(sceneCam); + renderer->SetPixelShader(rtdShader); + + //renderer.SetRenderState(IRenderState.AlphaBlendEnable, false); + //renderer.SetRenderState(IRenderState.AlphaTestEnable, false); + + renderer->SetTargets(NULL, depth); + LifeMath::float4 color = LifeMath::float4(1.0f, 1.0f, 1.0f, 1.0f); + renderer->ClearDepthStencil(depth,1.0f, 0); + renderer->SetBlendState(shadowBlendState); + } + + void UpdateBuffer() + { + PSSM_CONST c; + + /*float pssmSize; + float pssmNumSplits; + LifeMath::float3pssmCamPos; + LifeMath::float4x4 lmat[4]; + LifeMath::float4x4 pssmMat[4]; + LifeMath::float4 splitRegions[4]; + LifeMath::float4 splitRegionsSm[4];*/ + + c.pssmSize = static_cast(depthSize); + c.pssmMat[0] = lViews[0] * lProjs[0]; + c.pssmMat[1] = lViews[1] * lProjs[1]; + c.pssmMat[2] = lViews[2] * lProjs[2]; + + c.splitRegions[0] = splitRegions[0]; + c.splitRegions[1] = splitRegions[1]; + c.splitRegions[2] = splitRegions[2]; + + //printf("ololo: %i\n", sizeof(PSSM_CONST)); + + cbPssm->Update(&c, sizeof(PSSM_CONST)); + } + + void BeginSplit(int iSplit) + { + IRenderer* renderer = CG::me()->GetRenderer(); + + LifeMath::Rect2i scissor = LifeMath::Rect2i(0, 0, depthSize, depthSize); + renderer->SetScissorRectangle(scissor); + + fNear = fSplitDistances[iSplit]; + fFar = fSplitDistances[iSplit + 1]; + + float fScale = 1.1f; + + CalculateFrustumCorners(sceneCam->getPosition(), sceneCam->getDirection(), sceneCam->getUp(), + fNear, fFar, sceneCam->getFov(), sceneCam->getAspect(), fScale, vLightTarget); + + vLightSource = vLightTarget + light->getDirection() * 200.0f; + + vLightTarget = vLightTarget + light->getDirection() * -50.0f; + + LifeMath::float3 diff = points[6] - points[0]; + //CalculateLightForFrustum(vLightTarget); + float projSize = Vec3Length(&diff); + + Mat4x4LookAtLH(vLightSource, vLightTarget, LifeMath::float3(0.0f, 1.0f, 0.0f), mLightView); + Mat4x4Ortho(projSize, projSize, 0.1f, 350.0, mLightProj); + + float minDist = 100000.0f; + float maxDist = 0.0f; + + LifeMath::float4x4 mLightViewProj = mLightView * mLightProj; + + for(int i = 0; i< 8; i++) + { + LifeMath::float4 vTransformed; + + Vec3Transform(&points[i], &mLightViewProj, &vTransformed); + + float dist = vTransformed.Z; + if(minDist > dist) + minDist = dist; + if(maxDist < dist) + maxDist = dist; + } + + float vol = 1000.0f - 0.1f; + Mat4x4Ortho(projSize, projSize, 1.0f, maxDist * vol, mLightProj); + + f.UpdateFrustum(mLightView * mLightProj); + + //cam1.ViewMatrix = g_mLightView; + //cam1.ProjectionMatrix = g_mLightProj; + //cam1.Frustum = f; + //RenderQueue q = scene.CullScene(cam1, true, EffectType.SHADOW_MAPPING ); + + lViews[iSplit] = mLightView; + lProjs[iSplit] = mLightProj; + + LifeMath::float4 region = splitRegions[iSplit]; + + printf("CHECK ME TOO!!!\n"); + LifeMath::Rect2i tr =viewRegions[iSplit]; + renderer->SetViewport(tr); + + // renderer->SetViewport(splitRegions[iSplit].Region); + } + + void EndSplit() + { + } + + void End() + { + CG::me()->GetRenderer()->SetBlendState(restoreBlendState); + } + void CalculateSplitPlanes(const Camera* sceneCam) + { + fSplitSchemeLambda = clampf(fSplitSchemeLambda, 0.0f, 1.0f); + + for (int i = 0; i < splitCount; i++) + { + float fIDM = (float)i / (float)splitCount; + float fLog = sceneCam->getNear() * pow((sceneCam->getFar() / sceneCam->getNear()), fIDM); + float fUniform = sceneCam->getNear() + (sceneCam->getFar() - sceneCam->getNear()) * fIDM; + fSplitDistances[i] = fLog * fSplitSchemeLambda + fUniform * (1.0f - fSplitSchemeLambda); + + /* + float fIDM = (float)i / (float)splitCount; + float fLog = sceneCam->getNear() * pow((fLightFarMax / sceneCam->getNear()), fIDM); + float fUniform = sceneCam->getNear() + (fLightFarMax - sceneCam->getNear()) * fIDM; + fSplitDistances[i] = fLog * fSplitSchemeLambda + fUniform * (1.0f - fSplitSchemeLambda);*/ + } + + fSplitDistances[0] = sceneCam->getNear(); + fSplitDistances[splitCount] = sceneCam->getFar(); + } + + void CalculateLightForFrustum(LifeMath::float3 frustumCenter) + { + // calculate standard view and projection matrices for light + CalculateViewProj(vLightSource, vLightTarget, LifeMath::float3(0.0f, 1.0f, 0.0f), + fLightFOV, fLightNear, fLightFarMax, 1.0f); + + // Next we will find the min and max values of the current + // frustum split in lights post-projection space + // (where coordinate range is from -1.0 to 1.0) + // + float fMaxX = -10000000.0f; + float fMaxY = -10000000.0f; + float fMinX = 10000000.0f; + float fMinY = 10000000.0f; + float fMaxZ = 0.0f; + + LifeMath::float4x4 mLightViewProj = mLightView * mLightProj; + + // for each corner point + for (int i = 0; i < 8; i++) + { + // transform point + LifeMath::float4 vTransformed; + + Vec3Transform(&points[i], &mLightViewProj, &vTransformed); + + // project x and y + vTransformed.X /= vTransformed.W; + vTransformed.Y /= vTransformed.W; + + // find min and max values + if (vTransformed.X > fMaxX) fMaxX = vTransformed.X; + if (vTransformed.Y > fMaxY) fMaxY = vTransformed.Y; + if (vTransformed.Y < fMinY) fMinY = vTransformed.Y; + if (vTransformed.X < fMinX) fMinX = vTransformed.X; + + // find largest z distance + if (vTransformed.Z > fMaxZ) fMaxZ = vTransformed.Z; + } + + // set values to valid range (post-projection) + fMaxX = clampf(fMaxX, -1.0f, 1.0f); + fMaxY = clampf(fMaxY, -1.0f, 1.0f); + fMinX = clampf(fMinX, -1.0f, 1.0f); + fMinY = clampf(fMinY, -1.0f, 1.0f); + + // Adjust the far plane of the light to be at the farthest + // point of the frustum split. Some bias may be necessary. + // + fLightFar = fMaxZ + fLightNear + 1.5f; + //_fLightFar = fMaxZ + _fLightNear; + + // re-calculate lights matrices with the new far plane + CalculateViewProj(vLightSource, vLightTarget, LifeMath::float3(0.0f, 1.0f, 0.0f), + fLightFOV, fLightNear, fLightFar, 1.0f); + + // Next we build a special matrix for cropping the lights view + // to only contain points of the current frustum split + // + + LifeMath::float4 vTransCenter; + + Vec3Transform(&frustumCenter, &mLightViewProj, &vTransCenter); + + float fScaleX = 2.0f / (fMaxX - fMinX); + float fScaleY = 2.0f / (fMaxY - fMinY); + + float fOffsetX = -0.5f * (fMaxX + fMinX) * fScaleX + vTransCenter.X; + float fOffsetY = -0.5f * (fMaxY + fMinY) * fScaleY + vTransCenter.Y; + + LifeMath::float4x4 mCropView; + + mCropView.M11 = fScaleX; mCropView.M12 = 0.0f; mCropView.M13 = 0.0f; mCropView.M14 = 0.0f; + mCropView.M21 = 0.0f; mCropView.M22 = fScaleY; mCropView.M23 = 0.0f; mCropView.M24 = 0.0f; + mCropView.M31 = 0.0f; mCropView.M32 = 0.0f; mCropView.M33 = 1.0f; mCropView.M34 = 0.0f; + mCropView.M41 = fOffsetX; mCropView.M42 = fOffsetY; mCropView.M43 = 0.0f; mCropView.M44 = 1.0f; + + // multiply the projection matrix with it + mLightProj = mLightProj * mCropView; + + // finally modify projection matrix for linearized depth + mLightProj.M33 /= fLightFar; + mLightProj.M43 /= fLightFar; + } + + void CalculateViewProj(LifeMath::float3 vSource, LifeMath::float3 vTarget, LifeMath::float3 vUpVector, + float fFOV, float fNear, float fFar, float fAspect) + { + Mat4x4LookAtLH(vSource, vTarget, vUpVector, mLightView); + + Mat4x4PerspectiveFOV(fFOV, fAspect, fNear, fFar, mLightProj); + } + + void CalculateFrustumCorners(LifeMath::float3 vSource, LifeMath::float3 vTarget,LifeMath::float3 vUp,float fNear, float fFar, + float fFOV, float fAspect, float fScale, LifeMath::float3& center) + { + vTarget = vTarget + vSource; + + LifeMath::float3 vZ = vTarget - vSource; + LifeMath::Vec3Normalize(&vZ); + + LifeMath::float3 vX = LifeMath::Vec3Cross(vUp, vZ); + LifeMath::Vec3Normalize(&vX); + + LifeMath::float3 vY = LifeMath::Vec3Cross(vZ, vX); + + float fNearPlaneHeight = tanf(fFOV * 0.5f) * fNear; + float fNearPlaneWidth = fNearPlaneHeight * fAspect; + + float fFarPlaneHeight = tanf(fFOV * 0.5f) * fFar; + float fFarPlaneWidth = fFarPlaneHeight * fAspect; + + LifeMath::float3 vNearPlaneCenter = vSource + vZ * fNear; + LifeMath::float3 vFarPlaneCenter = vSource + vZ * fFar; + + points[0] = vNearPlaneCenter - vX * fNearPlaneWidth - vY * fNearPlaneHeight; + points[1] = vNearPlaneCenter - vX * fNearPlaneWidth + vY * fNearPlaneHeight; + points[2] = vNearPlaneCenter + vX * fNearPlaneWidth + vY * fNearPlaneHeight; + points[3] = vNearPlaneCenter + vX * fNearPlaneWidth - vY * fNearPlaneHeight; + + points[4] = vFarPlaneCenter - vX * fFarPlaneWidth - vY * fFarPlaneHeight; + points[5] = vFarPlaneCenter - vX * fFarPlaneWidth + vY * fFarPlaneHeight; + points[6] = vFarPlaneCenter + vX * fFarPlaneWidth + vY * fFarPlaneHeight; + points[7] = vFarPlaneCenter + vX * fFarPlaneWidth - vY * fFarPlaneHeight; + + // calculate center of points + LifeMath::float3 vCenter; + vCenter = LifeMath::float3(0.0f, 0.0f, 0.0f); + for (int i = 0; i < 8; i++) vCenter += points[i]; + vCenter /= 8.0f; + + // for each point + for (int i = 0; i < 8; i++) + { + // scale by adding offset from center + points[i] += (points[i] - vCenter) * (fScale - 1.0f); + } + center = vCenter; + } + }; +} +#endif // passPSSM_h__ \ No newline at end of file diff --git a/LifeGraphics/pugiconfig.hpp b/LifeGraphics/pugiconfig.hpp new file mode 100644 index 0000000..72b363a --- /dev/null +++ b/LifeGraphics/pugiconfig.hpp @@ -0,0 +1,62 @@ +/** +* pugixml parser - version 1.0 +* -------------------------------------------------------- +* Copyright (C) 2006-2010, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) +* Report bugs and download new versions at http://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 disable XPath +// #define PUGIXML_NO_XPATH + +// Uncomment this to disable STL +// Note: you can't use XPath with PUGIXML_NO_STL +// #define PUGIXML_NO_STL + +// Uncomment this to disable exceptions +// Note: you can't use XPath with PUGIXML_NO_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 + +#endif + +/** +* Copyright (c) 2006-2010 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. +*/ \ No newline at end of file diff --git a/LifeGraphics/pugixml.cpp b/LifeGraphics/pugixml.cpp new file mode 100644 index 0000000..30098ed --- /dev/null +++ b/LifeGraphics/pugixml.cpp @@ -0,0 +1,9574 @@ +/** +* pugixml parser - version 1.0 +* -------------------------------------------------------- +* Copyright (C) 2006-2010, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) +* Report bugs and download new versions at http://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) +*/ + +#include "pugixml.hpp" + +#include +#include +#include +#include +#include +#include + +#ifndef PUGIXML_NO_XPATH +# include +# include +#endif + +#ifndef PUGIXML_NO_STL +# include +# include +# include +#endif + +// For placement new +#include + +#ifdef _MSC_VER +# pragma warning(disable: 4127) // conditional expression is constant +# pragma warning(disable: 4324) // structure was padded due to __declspec(align()) +# pragma warning(disable: 4611) // interaction between '_setjmp' and C++ object destruction is non-portable +# pragma warning(disable: 4702) // unreachable code +# pragma warning(disable: 4996) // this function or variable may be unsafe +#endif + +#ifdef __INTEL_COMPILER +# pragma warning(disable: 177) // function was declared but never referenced +# pragma warning(disable: 1478 1786) // function was declared "deprecated" +#endif + +#ifdef __BORLANDC__ +# pragma warn -8008 // condition is always false +# pragma warn -8066 // unreachable code +#endif + +#ifdef __SNC__ +# pragma diag_suppress=178 // function was declared but never referenced +# pragma diag_suppress=237 // controlling expression is constant +#endif + +// uintptr_t +#if !defined(_MSC_VER) || _MSC_VER >= 1600 +# include +#else +# if _MSC_VER < 1300 +// No native uintptr_t in MSVC6 +typedef size_t uintptr_t; +# endif +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef __int32 int32_t; +#endif + +// Inlining controls +#if defined(_MSC_VER) && _MSC_VER >= 1300 +# define PUGIXML_NO_INLINE __declspec(noinline) +#elif defined(__GNUC__) +# define PUGIXML_NO_INLINE __attribute__((noinline)) +#else +# define PUGIXML_NO_INLINE +#endif + +// Simple static assertion +#define 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 DMC_VOLATILE volatile +#else +# define DMC_VOLATILE +#endif + +using namespace pugi; + +// Memory allocation +namespace +{ + void* default_allocate(size_t size) + { + return malloc(size); + } + + void default_deallocate(void* ptr) + { + free(ptr); + } + + allocation_function global_allocate = default_allocate; + deallocation_function global_deallocate = default_deallocate; +} + +// String utilities +namespace +{ + // Get string length + size_t strlength(const char_t* s) + { + assert(s); + +#ifdef PUGIXML_WCHAR_MODE + return wcslen(s); +#else + return strlen(s); +#endif + } + + // Compare two strings + 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) + 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; + } + +#ifdef PUGIXML_WCHAR_MODE + // Convert string to wide string, assuming all symbols are ASCII + void widen_ascii(wchar_t* dest, const char* source) + { + for (const char* i = source; *i; ++i) *dest++ = *i; + *dest = 0; + } +#endif +} + +#if !defined(PUGIXML_NO_STL) || !defined(PUGIXML_NO_XPATH) +// auto_ptr-like buffer holder for exception recovery +namespace +{ + struct buffer_holder + { + void* data; + void (*deleter)(void*); + + buffer_holder(void* data, void (*deleter)(void*)): data(data), deleter(deleter) + { + } + + ~buffer_holder() + { + if (data) deleter(data); + } + + void* release() + { + void* result = data; + data = 0; + return result; + } + }; +} +#endif + +namespace +{ + static const size_t xml_memory_page_size = 32768; + + static const uintptr_t xml_memory_page_alignment = 32; + static const uintptr_t xml_memory_page_pointer_mask = ~(xml_memory_page_alignment - 1); + static const uintptr_t xml_memory_page_name_allocated_mask = 16; + static const uintptr_t xml_memory_page_value_allocated_mask = 8; + static const uintptr_t xml_memory_page_type_mask = 7; + + struct xml_allocator; + + struct xml_memory_page + { + static xml_memory_page* construct(void* memory) + { + if (!memory) return 0; //$ redundant, left for performance + + xml_memory_page* result = static_cast(memory); + + result->allocator = 0; + result->memory = 0; + result->prev = 0; + result->next = 0; + result->busy_size = 0; + result->freed_size = 0; + + return result; + } + + xml_allocator* allocator; + + void* memory; + + xml_memory_page* prev; + xml_memory_page* next; + + size_t busy_size; + size_t freed_size; + + char data[1]; + }; + + 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) + { + } + + xml_memory_page* allocate_page(size_t data_size) + { + size_t size = offsetof(xml_memory_page, data) + data_size; + + // allocate block with some alignment, leaving memory for worst-case padding + void* memory = global_allocate(size + xml_memory_page_alignment); + if (!memory) return 0; + + // align upwards to page boundary + void* page_memory = reinterpret_cast((reinterpret_cast(memory) + (xml_memory_page_alignment - 1)) & ~(xml_memory_page_alignment - 1)); + + // prepare page structure + xml_memory_page* page = xml_memory_page::construct(page_memory); + + page->memory = memory; + page->allocator = _root->allocator; + + return page; + } + + static void deallocate_page(xml_memory_page* page) + { + global_deallocate(page->memory); + } + + void* allocate_memory_oob(size_t size, xml_memory_page*& out_page); + + void* allocate_memory(size_t size, xml_memory_page*& out_page) + { + if (_busy_size + size > xml_memory_page_size) return allocate_memory_oob(size, out_page); + + void* buf = _root->data + _busy_size; + + _busy_size += size; + + out_page = _root; + + return buf; + } + + void deallocate_memory(void* ptr, size_t size, xml_memory_page* page) + { + if (page == _root) page->busy_size = _busy_size; + + assert(ptr >= page->data && ptr < page->data + 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 = page->freed_size = 0; + _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) + { + // allocate memory for string and header block + size_t size = sizeof(xml_memory_string_header) + length * sizeof(char_t); + + // round size up to pointer alignment boundary + size_t full_size = (size + (sizeof(void*) - 1)) & ~(sizeof(void*) - 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) - page->data; + + assert(page_offset >= 0 && page_offset < (1 << 16)); + header->page_offset = static_cast(page_offset); + + // full_size == 0 for large strings that occupy the whole page + assert(full_size < (1 << 16) || (page->busy_size == full_size && page_offset == 0)); + header->full_size = static_cast(full_size < (1 << 16) ? full_size : 0); + + return reinterpret_cast(header + 1); + } + + void deallocate_string(char_t* string) + { + // get header + xml_memory_string_header* header = reinterpret_cast(string) - 1; + + // deallocate + size_t page_offset = offsetof(xml_memory_page, data) + header->page_offset; + xml_memory_page* page = reinterpret_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; + + deallocate_memory(header, full_size, page); + } + + xml_memory_page* _root; + size_t _busy_size; + }; + + PUGIXML_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); + 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; + } + + // allocate inside page + page->busy_size = size; + + out_page = page; + return page->data; + } +} + +namespace pugi +{ + /// A 'name=value' XML attribute structure. + struct xml_attribute_struct + { + /// Default ctor + xml_attribute_struct(xml_memory_page* page): header(reinterpret_cast(page)), name(0), value(0), prev_attribute_c(0), next_attribute(0) + { + } + + uintptr_t header; + + char_t* name; ///< Pointer to attribute name. + char_t* value; ///< Pointer to attribute value. + + xml_attribute_struct* prev_attribute_c; ///< Previous attribute (cyclic list) + xml_attribute_struct* next_attribute; ///< Next attribute + }; + + /// An XML document tree node. + struct xml_node_struct + { + /// Default ctor + /// \param type - node type + xml_node_struct(xml_memory_page* page, xml_node_type type): header(reinterpret_cast(page) | (type - 1)), parent(0), name(0), value(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0) + { + } + + uintptr_t header; + + xml_node_struct* parent; ///< Pointer to parent + + char_t* name; ///< Pointer to element name. + char_t* value; ///< Pointer to any associated string data. + + xml_node_struct* first_child; ///< First child + + xml_node_struct* prev_sibling_c; ///< Left brother (cyclic list) + xml_node_struct* next_sibling; ///< Right brother + + xml_attribute_struct* first_attribute; ///< First attribute + }; +} + +namespace +{ + 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) + { + } + + const char_t* buffer; + }; + + static inline xml_allocator& get_allocator(const xml_node_struct* node) + { + assert(node); + + return *reinterpret_cast(node->header & xml_memory_page_pointer_mask)->allocator; + } +} + +// Low-level DOM operations +namespace +{ + inline xml_attribute_struct* allocate_attribute(xml_allocator& alloc) + { + xml_memory_page* page; + void* memory = alloc.allocate_memory(sizeof(xml_attribute_struct), page); + + 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_memory(sizeof(xml_node_struct), page); + + return new (memory) xml_node_struct(page, type); + } + + inline void destroy_attribute(xml_attribute_struct* a, xml_allocator& alloc) + { + uintptr_t header = a->header; + + if (header & xml_memory_page_name_allocated_mask) alloc.deallocate_string(a->name); + if (header & xml_memory_page_value_allocated_mask) alloc.deallocate_string(a->value); + + alloc.deallocate_memory(a, sizeof(xml_attribute_struct), reinterpret_cast(header & xml_memory_page_pointer_mask)); + } + + inline void destroy_node(xml_node_struct* n, xml_allocator& alloc) + { + uintptr_t header = n->header; + + if (header & xml_memory_page_name_allocated_mask) alloc.deallocate_string(n->name); + if (header & 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), reinterpret_cast(header & xml_memory_page_pointer_mask)); + } + + PUGIXML_NO_INLINE xml_node_struct* append_node(xml_node_struct* node, xml_allocator& alloc, xml_node_type type = node_element) + { + xml_node_struct* child = allocate_node(alloc, type); + if (!child) return 0; + + child->parent = node; + + xml_node_struct* first_child = node->first_child; + + if (first_child) + { + xml_node_struct* last_child = first_child->prev_sibling_c; + + last_child->next_sibling = child; + child->prev_sibling_c = last_child; + first_child->prev_sibling_c = child; + } + else + { + node->first_child = child; + child->prev_sibling_c = child; + } + + return child; + } + + PUGIXML_NO_INLINE xml_attribute_struct* append_attribute_ll(xml_node_struct* node, xml_allocator& alloc) + { + xml_attribute_struct* a = allocate_attribute(alloc); + if (!a) return 0; + + xml_attribute_struct* first_attribute = node->first_attribute; + + if (first_attribute) + { + xml_attribute_struct* last_attribute = first_attribute->prev_attribute_c; + + last_attribute->next_attribute = a; + a->prev_attribute_c = last_attribute; + first_attribute->prev_attribute_c = a; + } + else + { + node->first_attribute = a; + a->prev_attribute_c = a; + } + + return a; + } +} + +// Helper classes for code generation +namespace +{ + struct opt_false + { + enum { value = 0 }; + }; + + struct opt_true + { + enum { value = 1 }; + }; +} + +// Unicode utilities +namespace +{ + 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 = (uint32_t)(ch - 0x10000) >> 10; + uint32_t lsh = (uint32_t)(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; + } + }; + + template struct wchar_selector; + + template <> struct wchar_selector<2> + { + typedef uint16_t type; + typedef utf16_counter counter; + typedef utf16_writer writer; + }; + + template <> struct wchar_selector<4> + { + typedef uint32_t type; + typedef utf32_counter counter; + typedef utf32_writer writer; + }; + + typedef wchar_selector::counter wchar_counter; + typedef wchar_selector::writer wchar_writer; + + template struct utf_decoder + { + static inline typename Traits::value_type decode_utf8_block(const uint8_t* data, size_t size, typename Traits::value_type result) + { + 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) + { + while (size >= 4 && (*reinterpret_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 ((unsigned)(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 ((unsigned)(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 ((unsigned)(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; + } + + static inline typename Traits::value_type decode_utf16_block(const uint16_t* data, size_t size, typename Traits::value_type result) + { + const uint16_t* end = data + size; + + while (data < end) + { + uint16_t lead = opt_swap::value ? endian_swap(*data) : *data; + + // U+0000..U+D7FF + if (lead < 0xD800) + { + result = Traits::low(result, lead); + data += 1; + } + // U+E000..U+FFFF + else if ((unsigned)(lead - 0xE000) < 0x2000) + { + result = Traits::low(result, lead); + data += 1; + } + // surrogate pair lead + else if ((unsigned)(lead - 0xD800) < 0x400 && data + 1 < end) + { + uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1]; + + if ((unsigned)(next - 0xDC00) < 0x400) + { + result = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff)); + data += 2; + } + else + { + data += 1; + } + } + else + { + data += 1; + } + } + + return result; + } + + static inline typename Traits::value_type decode_utf32_block(const uint32_t* data, size_t size, typename Traits::value_type result) + { + const uint32_t* end = data + size; + + while (data < end) + { + uint32_t lead = opt_swap::value ? endian_swap(*data) : *data; + + // U+0000..U+FFFF + if (lead < 0x10000) + { + result = Traits::low(result, lead); + data += 1; + } + // U+10000..U+10FFFF + else + { + result = Traits::high(result, lead); + data += 1; + } + } + + return result; + } + }; + + template inline void convert_utf_endian_swap(T* result, const T* data, size_t length) + { + for (size_t i = 0; i < length; ++i) result[i] = endian_swap(data[i]); + } + + inline 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]))); + } +} + +namespace +{ + 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, _, : + }; + + 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 (except \t), &, <, >, " + 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, _, -, . + }; + + const unsigned char chartypex_table[256] = + { + 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 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, 0, 0, 0, 0, 0, 0, 16, 16, 0, // 32-47 + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 3, 0, 3, 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 IS_CHARTYPE_IMPL(c, ct, table) ((static_cast(c) < 128 ? table[static_cast(c)] : table[128]) & (ct)) +#else +#define IS_CHARTYPE_IMPL(c, ct, table) (table[static_cast(c)] & (ct)) +#endif + +#define IS_CHARTYPE(c, ct) IS_CHARTYPE_IMPL(c, ct, chartype_table) +#define IS_CHARTYPEX(c, ct) IS_CHARTYPE_IMPL(c, ct, chartypex_table) + + bool is_little_endian() + { + unsigned int ui = 1; + + return *reinterpret_cast(&ui) == 1; + } + + xml_encoding get_wchar_encoding() + { + 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; + } + + xml_encoding guess_buffer_encoding(uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3) + { + // 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); + + DMC_VOLATILE uint8_t d0 = data[0], d1 = data[1], d2 = data[2], d3 = data[3]; + + return guess_buffer_encoding(d0, d1, d2, d3); + } + + bool get_mutable_buffer(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) + { + if (is_mutable) + { + out_buffer = static_cast(const_cast(contents)); + } + else + { + void* buffer = global_allocate(size > 0 ? size : 1); + if (!buffer) return false; + + memcpy(buffer, contents, size); + + out_buffer = static_cast(buffer); + } + + out_length = size / sizeof(char_t); + + return true; + } + +#ifdef PUGIXML_WCHAR_MODE + inline 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); + } + + 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); + + if (is_mutable) + { + out_buffer = const_cast(data); + } + else + { + out_buffer = static_cast(global_allocate(size > 0 ? size : 1)); + if (!out_buffer) return false; + } + + out_length = size / sizeof(char_t); + + convert_wchar_endian_swap(out_buffer, data, out_length); + + return true; + } + + bool convert_buffer_utf8(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size) + { + const uint8_t* data = static_cast(contents); + + // first pass: get length in wchar_t units + out_length = utf_decoder::decode_utf8_block(data, size, 0); + + // allocate buffer of suitable length + out_buffer = static_cast(global_allocate((out_length > 0 ? out_length : 1) * sizeof(char_t))); + if (!out_buffer) return false; + + // second pass: convert utf8 input to wchar_t + wchar_writer::value_type out_begin = reinterpret_cast(out_buffer); + wchar_writer::value_type out_end = utf_decoder::decode_utf8_block(data, size, out_begin); + + assert(out_end == out_begin + out_length); + (void)!out_end; + + return true; + } + + template bool convert_buffer_utf16(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) + { + const uint16_t* data = static_cast(contents); + size_t length = size / sizeof(uint16_t); + + // first pass: get length in wchar_t units + out_length = utf_decoder::decode_utf16_block(data, length, 0); + + // allocate buffer of suitable length + out_buffer = static_cast(global_allocate((out_length > 0 ? out_length : 1) * sizeof(char_t))); + if (!out_buffer) return false; + + // second pass: convert utf16 input to wchar_t + wchar_writer::value_type out_begin = reinterpret_cast(out_buffer); + wchar_writer::value_type out_end = utf_decoder::decode_utf16_block(data, length, out_begin); + + assert(out_end == out_begin + out_length); + (void)!out_end; + + return true; + } + + template bool convert_buffer_utf32(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) + { + const uint32_t* data = static_cast(contents); + size_t length = size / sizeof(uint32_t); + + // first pass: get length in wchar_t units + out_length = utf_decoder::decode_utf32_block(data, length, 0); + + // allocate buffer of suitable length + out_buffer = static_cast(global_allocate((out_length > 0 ? out_length : 1) * sizeof(char_t))); + if (!out_buffer) return false; + + // second pass: convert utf32 input to wchar_t + wchar_writer::value_type out_begin = reinterpret_cast(out_buffer); + wchar_writer::value_type out_end = utf_decoder::decode_utf32_block(data, length, out_begin); + + assert(out_end == out_begin + out_length); + (void)!out_end; + + return true; + } + + 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_utf8(out_buffer, out_length, contents, size); + + // 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_utf16(out_buffer, out_length, contents, size, opt_false()) : + convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true()); + } + + // 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_utf32(out_buffer, out_length, contents, size, opt_false()) : + convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true()); + } + + assert(!"Invalid encoding"); + return false; + } +#else + template bool convert_buffer_utf16(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) + { + const uint16_t* data = static_cast(contents); + size_t length = size / sizeof(uint16_t); + + // first pass: get length in utf8 units + out_length = utf_decoder::decode_utf16_block(data, length, 0); + + // allocate buffer of suitable length + out_buffer = static_cast(global_allocate((out_length > 0 ? out_length : 1) * sizeof(char_t))); + if (!out_buffer) return false; + + // second pass: convert utf16 input to utf8 + uint8_t* out_begin = reinterpret_cast(out_buffer); + uint8_t* out_end = utf_decoder::decode_utf16_block(data, length, out_begin); + + assert(out_end == out_begin + out_length); + (void)!out_end; + + return true; + } + + template bool convert_buffer_utf32(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) + { + const uint32_t* data = static_cast(contents); + size_t length = size / sizeof(uint32_t); + + // first pass: get length in utf8 units + out_length = utf_decoder::decode_utf32_block(data, length, 0); + + // allocate buffer of suitable length + out_buffer = static_cast(global_allocate((out_length > 0 ? out_length : 1) * sizeof(char_t))); + if (!out_buffer) return false; + + // second pass: convert utf32 input to utf8 + uint8_t* out_begin = reinterpret_cast(out_buffer); + uint8_t* out_end = utf_decoder::decode_utf32_block(data, length, out_begin); + + assert(out_end == out_begin + out_length); + (void)!out_end; + + return true; + } + + 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_utf16(out_buffer, out_length, contents, size, opt_false()) : + convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true()); + } + + // 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_utf32(out_buffer, out_length, contents, size, opt_false()) : + convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true()); + } + + assert(!"Invalid encoding"); + return false; + } +#endif + + size_t as_utf8_begin(const wchar_t* str, size_t length) + { + STATIC_ASSERT(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4); + + // get length in utf8 characters + return sizeof(wchar_t) == 2 ? + utf_decoder::decode_utf16_block(reinterpret_cast(str), length, 0) : + utf_decoder::decode_utf32_block(reinterpret_cast(str), length, 0); + } + + void as_utf8_end(char* buffer, size_t size, const wchar_t* str, size_t length) + { + STATIC_ASSERT(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4); + + // convert to utf8 + uint8_t* begin = reinterpret_cast(buffer); + uint8_t* end = sizeof(wchar_t) == 2 ? + utf_decoder::decode_utf16_block(reinterpret_cast(str), length, begin) : + utf_decoder::decode_utf32_block(reinterpret_cast(str), length, begin); + + assert(begin + size == end); + (void)!end; + + // zero-terminate + buffer[size] = 0; + } + +#ifndef PUGIXML_NO_STL + 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; + } + + std::wstring 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 = utf_decoder::decode_utf8_block(data, size, 0); + + // allocate resulting string + std::wstring 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 = utf_decoder::decode_utf8_block(data, size, begin); + + assert(begin + length == end); + (void)!end; + } + + return result; + } +#endif + + inline bool strcpy_insitu_allow(size_t length, uintptr_t allocated, char_t* target) + { + assert(target); + size_t target_length = strlength(target); + + // always reuse document buffer memory if possible + if (!allocated) 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); + } + + bool strcpy_insitu(char_t*& dest, uintptr_t& header, uintptr_t header_mask, const char_t* source) + { + size_t source_length = strlength(source); + + if (source_length == 0) + { + // empty string and null pointer are equivalent, so just deallocate old memory + xml_allocator* alloc = reinterpret_cast(header & xml_memory_page_pointer_mask)->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 + 1) * sizeof(char_t)); + + return true; + } + else + { + xml_allocator* alloc = reinterpret_cast(header & xml_memory_page_pointer_mask)->allocator; + + // 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 + 1) * sizeof(char_t)); + + // 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; + } + }; + + 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; + } + } + + return stre; + } + + // Utility macro for last character handling +#define ENDSWITH(c, e) ((c) == (e) || ((c) == 0 && endch == (e))) + + char_t* strconv_comment(char_t* s, char_t endch) + { + gap g; + + while (true) + { + while (!IS_CHARTYPE(*s, ct_parse_comment)) ++s; + + 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] == '-' && ENDSWITH(s[2], '>')) // comment ends here + { + *g.flush(s) = 0; + + return s + (s[2] == '>' ? 3 : 2); + } + else if (*s == 0) + { + return 0; + } + else ++s; + } + } + + char_t* strconv_cdata(char_t* s, char_t endch) + { + gap g; + + while (true) + { + while (!IS_CHARTYPE(*s, ct_parse_cdata)) ++s; + + 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] == ']' && 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; + + while (true) + { + while (!IS_CHARTYPE(*s, ct_parse_pcdata)) ++s; + + if (*s == '<') // PCDATA ends here + { + *g.flush(s) = 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) + { + return s; + } + else ++s; + } + } + }; + + strconv_pcdata_t get_strconv_pcdata(unsigned int optmask) + { + STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20); + + switch ((optmask >> 4) & 3) // get bitmask for flags (eol escapes) + { + 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; + default: return 0; // should not get here + } + } + + 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 (IS_CHARTYPE(*s, ct_space)) + { + char_t* str = s; + + do ++str; + while (IS_CHARTYPE(*str, ct_space)); + + g.push(s, str - s); + } + + while (true) + { + while (!IS_CHARTYPE(*s, ct_parse_attr_ws | ct_space)) ++s; + + if (*s == end_quote) + { + char_t* str = g.flush(s); + + do *str-- = 0; + while (IS_CHARTYPE(*str, ct_space)); + + return s + 1; + } + else if (IS_CHARTYPE(*s, ct_space)) + { + *s++ = ' '; + + if (IS_CHARTYPE(*s, ct_space)) + { + char_t* str = s + 1; + while (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) + { + while (!IS_CHARTYPE(*s, ct_parse_attr_ws)) ++s; + + if (*s == end_quote) + { + *g.flush(s) = 0; + + return s + 1; + } + else if (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) + { + while (!IS_CHARTYPE(*s, ct_parse_attr)) ++s; + + 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) + { + while (!IS_CHARTYPE(*s, ct_parse_attr)) ++s; + + 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; + } + } + }; + + strconv_attribute_t get_strconv_attribute(unsigned int optmask) + { + STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80); + + switch ((optmask >> 4) & 15) // get bitmask for flags (wconv wnorm eol escapes) + { + 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: return 0; // should not get here + } + } + + 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; + jmp_buf error_handler; + + // Parser utilities. +#define SKIPWS() { while (IS_CHARTYPE(*s, ct_space)) ++s; } +#define OPTSET(OPT) ( optmsk & OPT ) +#define PUSHNODE(TYPE) { cursor = append_node(cursor, alloc, TYPE); if (!cursor) THROW_ERROR(status_out_of_memory, s); } +#define POPNODE() { cursor = cursor->parent; } +#define SCANFOR(X) { while (*s != 0 && !(X)) ++s; } +#define SCANWHILE(X) { while ((X)) ++s; } +#define ENDSEG() { ch = *s; *s = 0; ++s; } +#define THROW_ERROR(err, m) error_offset = m, longjmp(error_handler, err) +#define CHECK_ERROR(err, m) { if (*s == 0) THROW_ERROR(err, m); } + + xml_parser(const xml_allocator& alloc): alloc(alloc), error_offset(0) + { + } + + // 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++; + SCANFOR(*s == ch); + if (!*s) THROW_ERROR(status_bad_doctype, s); + + s++; + } + else if (s[0] == '<' && s[1] == '?') + { + // + s += 2; + SCANFOR(s[0] == '?' && s[1] == '>'); // no need for ENDSWITH because ?> can't terminate proper doctype + if (!*s) THROW_ERROR(status_bad_doctype, s); + + s += 2; + } + else if (s[0] == '<' && s[1] == '!' && s[2] == '-' && s[3] == '-') + { + s += 4; + SCANFOR(s[0] == '-' && s[1] == '-' && s[2] == '>'); // no need for ENDSWITH because --> can't terminate proper doctype + if (!*s) THROW_ERROR(status_bad_doctype, s); + + s += 4; + } + else THROW_ERROR(status_bad_doctype, s); + + return s; + } + + char_t* parse_doctype_ignore(char_t* s) + { + assert(s[0] == '<' && s[1] == '!' && s[2] == '['); + s++; + + while (*s) + { + if (s[0] == '<' && s[1] == '!' && s[2] == '[') + { + // nested ignore section + s = parse_doctype_ignore(s); + } + else if (s[0] == ']' && s[1] == ']' && s[2] == '>') + { + // ignore section end + s += 3; + + return s; + } + else s++; + } + + THROW_ERROR(status_bad_doctype, s); + + return s; + } + + char_t* parse_doctype_group(char_t* s, char_t endch, bool toplevel) + { + assert(s[0] == '<' && s[1] == '!'); + s++; + + while (*s) + { + if (s[0] == '<' && s[1] == '!' && s[2] != '-') + { + if (s[2] == '[') + { + // ignore + s = parse_doctype_ignore(s); + } + else + { + // some control group + s = parse_doctype_group(s, endch, false); + } + } + else if (s[0] == '<' || s[0] == '"' || s[0] == '\'') + { + // unknown tag (forbidden), or some primitive group + s = parse_doctype_primitive(s); + } + else if (*s == '>') + { + s++; + + return s; + } + else s++; + } + + if (!toplevel || endch != '>') 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 (OPTSET(parse_eol) && OPTSET(parse_comments)) + { + s = strconv_comment(s, endch); + + if (!s) THROW_ERROR(status_bad_comment, cursor->value); + } + else + { + // Scan for terminating '-->'. + SCANFOR(s[0] == '-' && s[1] == '-' && ENDSWITH(s[2], '>')); + CHECK_ERROR(status_bad_comment, s); + + if (OPTSET(parse_comments)) + *s = 0; // Zero-terminate this segment at the first terminating '-'. + + s += (s[2] == '>' ? 3 : 2); // Step over the '\0->'. + } + } + else THROW_ERROR(status_bad_comment, s); + } + else if (*s == '[') + { + // '...' + if (*++s=='C' && *++s=='D' && *++s=='A' && *++s=='T' && *++s=='A' && *++s == '[') + { + ++s; + + if (OPTSET(parse_cdata)) + { + PUSHNODE(node_cdata); // Append a new node on the tree. + cursor->value = s; // Save the offset. + + if (OPTSET(parse_eol)) + { + s = strconv_cdata(s, endch); + + if (!s) THROW_ERROR(status_bad_cdata, cursor->value); + } + else + { + // Scan for terminating ''. + SCANFOR(s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>')); + 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 ']]>'. + SCANFOR(s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>')); + CHECK_ERROR(status_bad_cdata, s); + + ++s; + } + + s += (s[1] == '>' ? 2 : 1); // Step over the last ']>'. + } + else 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' && ENDSWITH(s[6], 'E')) + { + s -= 2; + + if (cursor->parent) THROW_ERROR(status_bad_doctype, s); + + char_t* mark = s + 9; + + s = parse_doctype_group(s, endch, true); + + if (OPTSET(parse_doctype)) + { + while (IS_CHARTYPE(*mark, ct_space)) ++mark; + + PUSHNODE(node_doctype); + + cursor->value = mark; + + assert((s[0] == 0 && endch == '>') || s[-1] == '>'); + s[*s == 0 ? 0 : -1] = 0; + + POPNODE(); + } + } + else if (*s == 0 && endch == '-') THROW_ERROR(status_bad_comment, s); + else if (*s == 0 && endch == '[') THROW_ERROR(status_bad_cdata, s); + else 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 (!IS_CHARTYPE(*s, ct_start_symbol)) THROW_ERROR(status_bad_pi, s); + + SCANWHILE(IS_CHARTYPE(*s, ct_symbol)); + 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 ? OPTSET(parse_declaration) : OPTSET(parse_pi)) + { + if (declaration) + { + // disallow non top-level declarations + if (cursor->parent) THROW_ERROR(status_bad_pi, s); + + PUSHNODE(node_declaration); + } + else + { + PUSHNODE(node_pi); + } + + cursor->name = target; + + ENDSEG(); + + // parse value/attributes + if (ch == '?') + { + // empty node + if (!ENDSWITH(*s, '>')) THROW_ERROR(status_bad_pi, s); + s += (*s == '>'); + + POPNODE(); + } + else if (IS_CHARTYPE(ch, ct_space)) + { + SKIPWS(); + + // scan for tag end + char_t* value = s; + + SCANFOR(s[0] == '?' && ENDSWITH(s[1], '>')); + 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; + POPNODE(); + + ENDSEG(); + + s += (*s == '>'); + } + } + else THROW_ERROR(status_bad_pi, s); + } + else + { + // scan for tag end + SCANFOR(s[0] == '?' && ENDSWITH(s[1], '>')); + CHECK_ERROR(status_bad_pi, s); + + s += (s[1] == '>' ? 2 : 1); + } + + // store from registers + ref_cursor = cursor; + + return s; + } + + void parse(char_t* s, xml_node_struct* xmldoc, 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 = xmldoc; + char_t* mark = s; + + while (*s != 0) + { + if (*s == '<') + { + ++s; + +LOC_TAG: + if (IS_CHARTYPE(*s, ct_start_symbol)) // '<#...' + { + PUSHNODE(node_element); // Append a new node to the tree. + + cursor->name = s; + + SCANWHILE(IS_CHARTYPE(*s, ct_symbol)); // Scan for a terminator. + ENDSEG(); // Save char in 'ch', terminate & step over. + + if (ch == '>') + { + // end of tag + } + else if (IS_CHARTYPE(ch, ct_space)) + { +LOC_ATTRIBUTES: + while (true) + { + SKIPWS(); // Eat any whitespace. + + if (IS_CHARTYPE(*s, ct_start_symbol)) // <... #... + { + xml_attribute_struct* a = append_attribute_ll(cursor, alloc); // Make space for this attribute. + if (!a) THROW_ERROR(status_out_of_memory, s); + + a->name = s; // Save the offset. + + SCANWHILE(IS_CHARTYPE(*s, ct_symbol)); // Scan for a terminator. + CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance + + ENDSEG(); // Save char in 'ch', terminate & step over. + CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance + + if (IS_CHARTYPE(ch, ct_space)) + { + SKIPWS(); // Eat any whitespace. + CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance + + ch = *s; + ++s; + } + + if (ch == '=') // '<... #=...' + { + 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) 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 (IS_CHARTYPE(*s, ct_start_symbol)) THROW_ERROR(status_bad_attribute, s); + } + else THROW_ERROR(status_bad_attribute, s); + } + else THROW_ERROR(status_bad_attribute, s); + } + else if (*s == '/') + { + ++s; + + if (*s == '>') + { + POPNODE(); + s++; + break; + } + else if (*s == 0 && endch == '>') + { + POPNODE(); + break; + } + else THROW_ERROR(status_bad_start_element, s); + } + else if (*s == '>') + { + ++s; + + break; + } + else if (*s == 0 && endch == '>') + { + break; + } + else THROW_ERROR(status_bad_start_element, s); + } + + // !!! + } + else if (ch == '/') // '<#.../' + { + if (!ENDSWITH(*s, '>')) THROW_ERROR(status_bad_start_element, s); + + POPNODE(); // Pop. + + s += (*s == '>'); + } + else if (ch == 0) + { + // we stepped over null terminator, backtrack & handle closing tag + --s; + + if (endch != '>') THROW_ERROR(status_bad_start_element, s); + } + else THROW_ERROR(status_bad_start_element, s); + } + else if (*s == '/') + { + ++s; + + char_t* name = cursor->name; + if (!name) THROW_ERROR(status_end_element_mismatch, s); + + while (IS_CHARTYPE(*s, ct_symbol)) + { + if (*s++ != *name++) THROW_ERROR(status_end_element_mismatch, s); + } + + if (*name) + { + if (*s == 0 && name[0] == endch && name[1] == 0) THROW_ERROR(status_bad_end_element, s); + else THROW_ERROR(status_end_element_mismatch, s); + } + + POPNODE(); // Pop. + + SKIPWS(); + + if (*s == 0) + { + if (endch != '>') THROW_ERROR(status_bad_end_element, s); + } + else + { + if (*s != '>') THROW_ERROR(status_bad_end_element, s); + ++s; + } + } + else if (*s == '?') // 'header & xml_memory_page_type_mask) + 1 == node_declaration) goto LOC_ATTRIBUTES; + } + else if (*s == '!') // 'parent) + { + PUSHNODE(node_pcdata); // Append a new node on the tree. + cursor->value = s; // Save the offset. + + s = strconv_pcdata(s); + + POPNODE(); // Pop since this is a standalone. + + if (!*s) break; + } + else + { + SCANFOR(*s == '<'); // '...<' + if (!*s) break; + + ++s; + } + + // We're after '<' + goto LOC_TAG; + } + } + + // check that last tag is closed + if (cursor != xmldoc) THROW_ERROR(status_end_element_mismatch, s); + } + + static xml_parse_result parse(char_t* buffer, size_t length, xml_node_struct* root, unsigned int optmsk) + { + xml_document_struct* xmldoc = static_cast(root); + + // store buffer for offset_debug + xmldoc->buffer = buffer; + + // early-out for empty documents + if (length == 0) return make_parse_result(status_ok); + + // create parser on stack + xml_parser parser(*xmldoc); + + // save last character and make buffer zero-terminated (speeds up parsing) + char_t endch = buffer[length - 1]; + buffer[length - 1] = 0; + + // perform actual parsing + int error = setjmp(parser.error_handler); + + if (error == 0) + { + parser.parse(buffer, xmldoc, optmsk, endch); + } + + xml_parse_result result = make_parse_result(static_cast(error), parser.error_offset ? parser.error_offset - buffer : 0); + assert(result.offset >= 0 && static_cast(result.offset) <= length); + + // update allocator state + *static_cast(xmldoc) = parser.alloc; + + // since we removed last character, we have to handle the only possible false positive + if (result && endch == '<') + { + // there's no possible well-formed document with < at the end + return make_parse_result(status_unrecognized_tag, length); + } + + return result; + } + }; + + // Output facilities + xml_encoding get_write_native_encoding() + { +#ifdef PUGIXML_WCHAR_MODE + return get_wchar_encoding(); +#else + return encoding_utf8; +#endif + } + + 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; + } + +#ifdef PUGIXML_WCHAR_MODE + size_t get_valid_length(const char_t* data, size_t length) + { + assert(length > 0); + + // discard last character if it's the lead of a surrogate pair + return (sizeof(wchar_t) == 2 && (unsigned)(static_cast(data[length - 1]) - 0xD800) < 0x400) ? length - 1 : length; + } + + size_t convert_buffer(char* result, 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(reinterpret_cast(result), data, length); + + return length * sizeof(char_t); + } + + // convert to utf8 + if (encoding == encoding_utf8) + { + uint8_t* dest = reinterpret_cast(result); + + uint8_t* end = sizeof(wchar_t) == 2 ? + utf_decoder::decode_utf16_block(reinterpret_cast(data), length, dest) : + utf_decoder::decode_utf32_block(reinterpret_cast(data), length, dest); + + return static_cast(end - dest); + } + + // convert to utf16 + if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) + { + uint16_t* dest = reinterpret_cast(result); + + // convert to native utf16 + uint16_t* end = utf_decoder::decode_utf32_block(reinterpret_cast(data), length, dest); + + // swap if necessary + xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; + + if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); + + return static_cast(end - dest) * sizeof(uint16_t); + } + + // convert to utf32 + if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) + { + uint32_t* dest = reinterpret_cast(result); + + // convert to native utf32 + uint32_t* end = utf_decoder::decode_utf16_block(reinterpret_cast(data), length, dest); + + // swap if necessary + xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; + + if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); + + return static_cast(end - dest) * sizeof(uint32_t); + } + + assert(!"Invalid encoding"); + return 0; + } +#else + size_t get_valid_length(const char_t* data, size_t length) + { + assert(length > 4); + + 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; + } + + size_t convert_buffer(char* result, const char_t* data, size_t length, xml_encoding encoding) + { + if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) + { + uint16_t* dest = reinterpret_cast(result); + + // convert to native utf16 + uint16_t* end = utf_decoder::decode_utf8_block(reinterpret_cast(data), length, dest); + + // swap if necessary + xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; + + if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); + + return static_cast(end - dest) * sizeof(uint16_t); + } + + if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) + { + uint32_t* dest = reinterpret_cast(result); + + // convert to native utf32 + uint32_t* end = utf_decoder::decode_utf8_block(reinterpret_cast(data), length, dest); + + // swap if necessary + xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; + + if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); + + return static_cast(end - dest) * sizeof(uint32_t); + } + + assert(!"Invalid encoding"); + 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)) + { + } + + ~xml_buffered_writer() + { + flush(); + } + + void flush() + { + flush(buffer, bufsize); + bufsize = 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(scratch, data, size, encoding); + assert(result <= sizeof(scratch)); + + // write data + writer.write(scratch, result); + } + } + + void write(const char_t* data, size_t length) + { + if (bufsize + length > bufcapacity) + { + // 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); + + // 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(const char_t* data) + { + write(data, strlength(data)); + } + + void write(char_t d0) + { + if (bufsize + 1 > bufcapacity) flush(); + + buffer[bufsize + 0] = d0; + bufsize += 1; + } + + void write(char_t d0, char_t d1) + { + if (bufsize + 2 > bufcapacity) flush(); + + buffer[bufsize + 0] = d0; + buffer[bufsize + 1] = d1; + bufsize += 2; + } + + void write(char_t d0, char_t d1, char_t d2) + { + if (bufsize + 3 > bufcapacity) flush(); + + buffer[bufsize + 0] = d0; + buffer[bufsize + 1] = d1; + buffer[bufsize + 2] = d2; + bufsize += 3; + } + + void write(char_t d0, char_t d1, char_t d2, char_t d3) + { + if (bufsize + 4 > bufcapacity) flush(); + + buffer[bufsize + 0] = d0; + buffer[bufsize + 1] = d1; + buffer[bufsize + 2] = d2; + buffer[bufsize + 3] = d3; + bufsize += 4; + } + + void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4) + { + if (bufsize + 5 > bufcapacity) flush(); + + buffer[bufsize + 0] = d0; + buffer[bufsize + 1] = d1; + buffer[bufsize + 2] = d2; + buffer[bufsize + 3] = d3; + buffer[bufsize + 4] = d4; + bufsize += 5; + } + + void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4, char_t d5) + { + if (bufsize + 6 > bufcapacity) flush(); + + buffer[bufsize + 0] = d0; + buffer[bufsize + 1] = d1; + buffer[bufsize + 2] = d2; + buffer[bufsize + 3] = d3; + buffer[bufsize + 4] = d4; + buffer[bufsize + 5] = d5; + bufsize += 6; + } + + // utf8 maximum expansion: x4 (-> utf32) + // utf16 maximum expansion: x2 (-> utf32) + // utf32 maximum expansion: x1 + enum { bufcapacity = 2048 }; + + char_t buffer[bufcapacity]; + char scratch[4 * bufcapacity]; + + xml_writer& writer; + size_t bufsize; + xml_encoding encoding; + }; + + void write_bom(xml_writer& writer, xml_encoding encoding) + { + switch (encoding) + { + case encoding_utf8: + writer.write("\xef\xbb\xbf", 3); + break; + + case encoding_utf16_be: + writer.write("\xfe\xff", 2); + break; + + case encoding_utf16_le: + writer.write("\xff\xfe", 2); + break; + + case encoding_utf32_be: + writer.write("\x00\x00\xfe\xff", 4); + break; + + case encoding_utf32_le: + writer.write("\xff\xfe\x00\x00", 4); + break; + + default: + assert(!"Invalid encoding"); + } + } + + void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type) + { + while (*s) + { + const char_t* prev = s; + + // While *s is a usual symbol + while (!IS_CHARTYPEX(*s, type)) ++s; + + writer.write(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 '"': + writer.write('&', 'q', 'u', 'o', 't', ';'); + ++s; + break; + default: // s is not a usual symbol + { + unsigned int ch = static_cast(*s++); + assert(ch < 32); + + writer.write('&', '#', static_cast((ch / 10) + '0'), static_cast((ch % 10) + '0'), ';'); + } + } + } + } + + 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(prev, static_cast(s - prev)); + + writer.write(']', ']', '>'); + } + while (*s); + } + + void node_output_attributes(xml_buffered_writer& writer, const xml_node& node) + { + const char_t* default_name = PUGIXML_TEXT(":anonymous"); + + for (xml_attribute a = node.first_attribute(); a; a = a.next_attribute()) + { + writer.write(' '); + writer.write(a.name()[0] ? a.name() : default_name); + writer.write('=', '"'); + + text_output_escaped(writer, a.value(), ctx_special_attr); + + writer.write('"'); + } + } + + void node_output(xml_buffered_writer& writer, const xml_node& node, const char_t* indent, unsigned int flags, unsigned int depth) + { + const char_t* default_name = PUGIXML_TEXT(":anonymous"); + + if ((flags & format_indent) != 0 && (flags & format_raw) == 0) + for (unsigned int i = 0; i < depth; ++i) writer.write(indent); + + switch (node.type()) + { + case node_document: + { + for (xml_node n = node.first_child(); n; n = n.next_sibling()) + node_output(writer, n, indent, flags, depth); + break; + } + + case node_element: + { + const char_t* name = node.name()[0] ? node.name() : default_name; + + writer.write('<'); + writer.write(name); + + node_output_attributes(writer, node); + + if (flags & format_raw) + { + if (!node.first_child()) + writer.write(' ', '/', '>'); + else + { + writer.write('>'); + + for (xml_node n = node.first_child(); n; n = n.next_sibling()) + node_output(writer, n, indent, flags, depth + 1); + + writer.write('<', '/'); + writer.write(name); + writer.write('>'); + } + } + else if (!node.first_child()) + writer.write(' ', '/', '>', '\n'); + else if (node.first_child() == node.last_child() && (node.first_child().type() == node_pcdata || node.first_child().type() == node_cdata)) + { + writer.write('>'); + + if (node.first_child().type() == node_pcdata) + text_output_escaped(writer, node.first_child().value(), ctx_special_pcdata); + else + text_output_cdata(writer, node.first_child().value()); + + writer.write('<', '/'); + writer.write(name); + writer.write('>', '\n'); + } + else + { + writer.write('>', '\n'); + + for (xml_node n = node.first_child(); n; n = n.next_sibling()) + node_output(writer, n, indent, flags, depth + 1); + + if ((flags & format_indent) != 0 && (flags & format_raw) == 0) + for (unsigned int i = 0; i < depth; ++i) writer.write(indent); + + writer.write('<', '/'); + writer.write(name); + writer.write('>', '\n'); + } + + break; + } + + case node_pcdata: + text_output_escaped(writer, node.value(), ctx_special_pcdata); + if ((flags & format_raw) == 0) writer.write('\n'); + break; + + case node_cdata: + text_output_cdata(writer, node.value()); + if ((flags & format_raw) == 0) writer.write('\n'); + break; + + case node_comment: + writer.write('<', '!', '-', '-'); + writer.write(node.value()); + writer.write('-', '-', '>'); + if ((flags & format_raw) == 0) writer.write('\n'); + break; + + case node_pi: + case node_declaration: + writer.write('<', '?'); + writer.write(node.name()[0] ? node.name() : default_name); + + if (node.type() == node_declaration) + { + node_output_attributes(writer, node); + } + else if (node.value()[0]) + { + writer.write(' '); + writer.write(node.value()); + } + + writer.write('?', '>'); + if ((flags & format_raw) == 0) writer.write('\n'); + break; + + case node_doctype: + writer.write('<', '!', 'D', 'O', 'C'); + writer.write('T', 'Y', 'P', 'E'); + + if (node.value()[0]) + { + writer.write(' '); + writer.write(node.value()); + } + + writer.write('>'); + if ((flags & format_raw) == 0) writer.write('\n'); + break; + + default: + assert(!"Invalid node type"); + } + } + + inline bool has_declaration(const xml_node& node) + { + for (xml_node child = node.first_child(); child; child = child.next_sibling()) + { + xml_node_type type = child.type(); + + if (type == node_declaration) return true; + if (type == node_element) return false; + } + + return false; + } + + inline 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; + } + + void recursive_copy_skip(xml_node& dest, const xml_node& source, const xml_node& skip) + { + assert(dest.type() == source.type()); + + switch (source.type()) + { + case node_element: + { + dest.set_name(source.name()); + + for (xml_attribute a = source.first_attribute(); a; a = a.next_attribute()) + dest.append_attribute(a.name()).set_value(a.value()); + + for (xml_node c = source.first_child(); c; c = c.next_sibling()) + { + if (c == skip) continue; + + xml_node cc = dest.append_child(c.type()); + assert(cc); + + recursive_copy_skip(cc, c, skip); + } + + break; + } + + case node_pcdata: + case node_cdata: + case node_comment: + case node_doctype: + dest.set_value(source.value()); + break; + + case node_pi: + dest.set_name(source.name()); + dest.set_value(source.value()); + break; + + case node_declaration: + { + dest.set_name(source.name()); + + for (xml_attribute a = source.first_attribute(); a; a = a.next_attribute()) + dest.append_attribute(a.name()).set_value(a.value()); + + break; + } + + default: + assert(!"Invalid node type"); + } + } + + // 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 + xml_parse_status get_file_size(FILE* file, size_t& out_result) + { +#if defined(_MSC_VER) && _MSC_VER >= 1400 + // 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__) + // 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; + } + + xml_parse_result load_file_impl(xml_document& doc, FILE* file, unsigned int options, xml_encoding encoding) + { + 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) + { + fclose(file); + return make_parse_result(size_status); + } + + // allocate buffer for the whole file + char* contents = static_cast(global_allocate(size > 0 ? size : 1)); + + if (!contents) + { + fclose(file); + return make_parse_result(status_out_of_memory); + } + + // read file in memory + size_t read_size = fread(contents, 1, size, file); + fclose(file); + + if (read_size != size) + { + global_deallocate(contents); + return make_parse_result(status_io_error); + } + + return doc.load_buffer_inplace_own(contents, size, options, encoding); + } + +#ifndef PUGIXML_NO_STL + template xml_parse_result load_stream_impl(xml_document& doc, std::basic_istream& stream, unsigned int options, xml_encoding encoding) + { + // 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 make_parse_result(status_io_error); + + // guard against huge files + size_t read_length = static_cast(length); + + if (static_cast(read_length) != length || length < 0) return make_parse_result(status_out_of_memory); + + // read stream data into memory (guard against stream exceptions with buffer holder) + buffer_holder buffer(global_allocate((read_length > 0 ? read_length : 1) * sizeof(T)), global_deallocate); + if (!buffer.data) return make_parse_result(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()) return make_parse_result(status_io_error); + + // load data from buffer + size_t actual_length = static_cast(stream.gcount()); + assert(actual_length <= read_length); + + return doc.load_buffer_inplace_own(buffer.release(), actual_length * sizeof(T), options, encoding); + } +#endif + +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__MINGW32__) + FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) + { + return _wfopen(path, mode); + } +#else + char* convert_path_heap(const wchar_t* str) + { + assert(str); + + // first pass: get length in utf8 characters + size_t length = wcslen(str); + size_t size = as_utf8_begin(str, length); + + // allocate resulting string + char* result = static_cast(global_allocate(size + 1)); + if (!result) return 0; + + // second pass: convert to utf8 + as_utf8_end(result, size, str, length); + + return result; + } + + 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 + global_deallocate(path_utf8); + + return result; + } +#endif +} + +namespace pugi +{ + xml_writer_file::xml_writer_file(void* file): file(file) + { + } + + void xml_writer_file::write(const void* data, size_t size) + { + fwrite(data, size, 1, static_cast(file)); + } + +#ifndef PUGIXML_NO_STL + xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(&stream), wide_stream(0) + { + } + + xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(0), wide_stream(&stream) + { + } + + 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 + + xml_tree_walker::xml_tree_walker(): _depth(0) + { + } + + xml_tree_walker::~xml_tree_walker() + { + } + + int xml_tree_walker::depth() const + { + return _depth; + } + + bool xml_tree_walker::begin(xml_node&) + { + return true; + } + + bool xml_tree_walker::end(xml_node&) + { + return true; + } + + xml_attribute::xml_attribute(): _attr(0) + { + } + + xml_attribute::xml_attribute(xml_attribute_struct* attr): _attr(attr) + { + } + + xml_attribute::operator xml_attribute::unspecified_bool_type() const + { + return _attr ? &xml_attribute::_attr : 0; + } + + bool xml_attribute::operator!() const + { + return !_attr; + } + + bool xml_attribute::operator==(const xml_attribute& r) const + { + return (_attr == r._attr); + } + + bool xml_attribute::operator!=(const xml_attribute& r) const + { + return (_attr != r._attr); + } + + bool xml_attribute::operator<(const xml_attribute& r) const + { + return (_attr < r._attr); + } + + bool xml_attribute::operator>(const xml_attribute& r) const + { + return (_attr > r._attr); + } + + bool xml_attribute::operator<=(const xml_attribute& r) const + { + return (_attr <= r._attr); + } + + bool xml_attribute::operator>=(const xml_attribute& r) const + { + return (_attr >= r._attr); + } + + xml_attribute xml_attribute::next_attribute() const + { + return _attr ? xml_attribute(_attr->next_attribute) : xml_attribute(); + } + + xml_attribute xml_attribute::previous_attribute() const + { + return _attr && _attr->prev_attribute_c->next_attribute ? xml_attribute(_attr->prev_attribute_c) : xml_attribute(); + } + + int xml_attribute::as_int() const + { + if (!_attr || !_attr->value) return 0; + +#ifdef PUGIXML_WCHAR_MODE + return (int)wcstol(_attr->value, 0, 10); +#else + return (int)strtol(_attr->value, 0, 10); +#endif + } + + unsigned int xml_attribute::as_uint() const + { + if (!_attr || !_attr->value) return 0; + +#ifdef PUGIXML_WCHAR_MODE + return (unsigned int)wcstoul(_attr->value, 0, 10); +#else + return (unsigned int)strtoul(_attr->value, 0, 10); +#endif + } + + double xml_attribute::as_double() const + { + if (!_attr || !_attr->value) return 0; + +#ifdef PUGIXML_WCHAR_MODE + return wcstod(_attr->value, 0); +#else + return strtod(_attr->value, 0); +#endif + } + + float xml_attribute::as_float() const + { + if (!_attr || !_attr->value) return 0; + +#ifdef PUGIXML_WCHAR_MODE + return (float)wcstod(_attr->value, 0); +#else + return (float)strtod(_attr->value, 0); +#endif + } + + bool xml_attribute::as_bool() const + { + if (!_attr || !_attr->value) return false; + + // only look at first char + char_t first = *_attr->value; + + // 1*, t* (true), T* (True), y* (yes), Y* (YES) + return (first == '1' || first == 't' || first == 'T' || first == 'y' || first == 'Y'); + } + + bool xml_attribute::empty() const + { + return !_attr; + } + + const char_t* xml_attribute::name() const + { + return (_attr && _attr->name) ? _attr->name : PUGIXML_TEXT(""); + } + + const char_t* xml_attribute::value() const + { + return (_attr && _attr->value) ? _attr->value : PUGIXML_TEXT(""); + } + + size_t xml_attribute::hash_value() const + { + return static_cast(reinterpret_cast(_attr) / sizeof(xml_attribute_struct)); + } + + xml_attribute_struct* xml_attribute::internal_object() const + { + return _attr; + } + + xml_attribute& xml_attribute::operator=(const char_t* rhs) + { + set_value(rhs); + return *this; + } + + xml_attribute& xml_attribute::operator=(int rhs) + { + set_value(rhs); + return *this; + } + + xml_attribute& xml_attribute::operator=(unsigned int rhs) + { + set_value(rhs); + return *this; + } + + xml_attribute& xml_attribute::operator=(double rhs) + { + set_value(rhs); + return *this; + } + + xml_attribute& xml_attribute::operator=(bool rhs) + { + set_value(rhs); + return *this; + } + + bool xml_attribute::set_name(const char_t* rhs) + { + if (!_attr) return false; + + return strcpy_insitu(_attr->name, _attr->header, xml_memory_page_name_allocated_mask, rhs); + } + + bool xml_attribute::set_value(const char_t* rhs) + { + if (!_attr) return false; + + return strcpy_insitu(_attr->value, _attr->header, xml_memory_page_value_allocated_mask, rhs); + } + + bool xml_attribute::set_value(int rhs) + { + char buf[128]; + sprintf(buf, "%d", rhs); + +#ifdef PUGIXML_WCHAR_MODE + char_t wbuf[128]; + widen_ascii(wbuf, buf); + + return set_value(wbuf); +#else + return set_value(buf); +#endif + } + + bool xml_attribute::set_value(unsigned int rhs) + { + char buf[128]; + sprintf(buf, "%u", rhs); + +#ifdef PUGIXML_WCHAR_MODE + char_t wbuf[128]; + widen_ascii(wbuf, buf); + + return set_value(wbuf); +#else + return set_value(buf); +#endif + } + + bool xml_attribute::set_value(double rhs) + { + char buf[128]; + sprintf(buf, "%g", rhs); + +#ifdef PUGIXML_WCHAR_MODE + char_t wbuf[128]; + widen_ascii(wbuf, buf); + + return set_value(wbuf); +#else + return set_value(buf); +#endif + } + + bool xml_attribute::set_value(bool rhs) + { + return set_value(rhs ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false")); + } + +#ifdef __BORLANDC__ + bool operator&&(const xml_attribute& lhs, bool rhs) + { + return (bool)lhs && rhs; + } + + bool operator||(const xml_attribute& lhs, bool rhs) + { + return (bool)lhs || rhs; + } +#endif + + xml_node::xml_node(): _root(0) + { + } + + xml_node::xml_node(xml_node_struct* p): _root(p) + { + } + + xml_node::operator xml_node::unspecified_bool_type() const + { + return _root ? &xml_node::_root : 0; + } + + bool xml_node::operator!() const + { + return !_root; + } + + xml_node::iterator xml_node::begin() const + { + return iterator(_root ? _root->first_child : 0, _root); + } + + xml_node::iterator xml_node::end() const + { + return iterator(0, _root); + } + + xml_node::attribute_iterator xml_node::attributes_begin() const + { + return attribute_iterator(_root ? _root->first_attribute : 0, _root); + } + + xml_node::attribute_iterator xml_node::attributes_end() const + { + return attribute_iterator(0, _root); + } + + bool xml_node::operator==(const xml_node& r) const + { + return (_root == r._root); + } + + bool xml_node::operator!=(const xml_node& r) const + { + return (_root != r._root); + } + + bool xml_node::operator<(const xml_node& r) const + { + return (_root < r._root); + } + + bool xml_node::operator>(const xml_node& r) const + { + return (_root > r._root); + } + + bool xml_node::operator<=(const xml_node& r) const + { + return (_root <= r._root); + } + + bool xml_node::operator>=(const xml_node& r) const + { + return (_root >= r._root); + } + + bool xml_node::empty() const + { + return !_root; + } + + const char_t* xml_node::name() const + { + return (_root && _root->name) ? _root->name : PUGIXML_TEXT(""); + } + + xml_node_type xml_node::type() const + { + return _root ? static_cast((_root->header & xml_memory_page_type_mask) + 1) : node_null; + } + + const char_t* xml_node::value() const + { + return (_root && _root->value) ? _root->value : PUGIXML_TEXT(""); + } + + 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 && strequal(name, i->name)) return xml_node(i); + + return xml_node(); + } + + 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 && strequal(name, i->name)) + return xml_attribute(i); + + return xml_attribute(); + } + + 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 && strequal(name, i->name)) return xml_node(i); + + return xml_node(); + } + + xml_node xml_node::next_sibling() const + { + if (!_root) return xml_node(); + + if (_root->next_sibling) return xml_node(_root->next_sibling); + else return xml_node(); + } + + 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 && strequal(name, i->name)) return xml_node(i); + + return xml_node(); + } + + 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(); + } + + xml_node xml_node::parent() const + { + return _root ? xml_node(_root->parent) : xml_node(); + } + + xml_node xml_node::root() const + { + if (!_root) return xml_node(); + + xml_memory_page* page = reinterpret_cast(_root->header & xml_memory_page_pointer_mask); + + return xml_node(static_cast(page->allocator)); + } + + const char_t* xml_node::child_value() const + { + if (!_root) return PUGIXML_TEXT(""); + + for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) + { + xml_node_type type = static_cast((i->header & xml_memory_page_type_mask) + 1); + + if (i->value && (type == node_pcdata || type == node_cdata)) + return i->value; + } + + return PUGIXML_TEXT(""); + } + + const char_t* xml_node::child_value(const char_t* name) const + { + return child(name).child_value(); + } + + xml_attribute xml_node::first_attribute() const + { + return _root ? xml_attribute(_root->first_attribute) : xml_attribute(); + } + + xml_attribute xml_node::last_attribute() const + { + return _root && _root->first_attribute ? xml_attribute(_root->first_attribute->prev_attribute_c) : xml_attribute(); + } + + xml_node xml_node::first_child() const + { + return _root ? xml_node(_root->first_child) : xml_node(); + } + + xml_node xml_node::last_child() const + { + return _root && _root->first_child ? xml_node(_root->first_child->prev_sibling_c) : xml_node(); + } + + bool xml_node::set_name(const char_t* rhs) + { + switch (type()) + { + case node_pi: + case node_declaration: + case node_element: + return strcpy_insitu(_root->name, _root->header, xml_memory_page_name_allocated_mask, rhs); + + default: + return false; + } + } + + bool xml_node::set_value(const char_t* rhs) + { + switch (type()) + { + case node_pi: + case node_cdata: + case node_pcdata: + case node_comment: + case node_doctype: + return strcpy_insitu(_root->value, _root->header, xml_memory_page_value_allocated_mask, rhs); + + default: + return false; + } + } + + xml_attribute xml_node::append_attribute(const char_t* name) + { + if (type() != node_element && type() != node_declaration) return xml_attribute(); + + xml_attribute a(append_attribute_ll(_root, get_allocator(_root))); + a.set_name(name); + + return a; + } + + xml_attribute xml_node::prepend_attribute(const char_t* name) + { + if (type() != node_element && type() != node_declaration) return xml_attribute(); + + xml_attribute a(allocate_attribute(get_allocator(_root))); + if (!a) return xml_attribute(); + + a.set_name(name); + + xml_attribute_struct* head = _root->first_attribute; + + if (head) + { + a._attr->prev_attribute_c = head->prev_attribute_c; + head->prev_attribute_c = a._attr; + } + else + a._attr->prev_attribute_c = a._attr; + + a._attr->next_attribute = head; + _root->first_attribute = a._attr; + + return a; + } + + xml_attribute xml_node::insert_attribute_before(const char_t* name, const xml_attribute& attr) + { + if ((type() != node_element && type() != node_declaration) || attr.empty()) return xml_attribute(); + + // check that attribute belongs to *this + xml_attribute_struct* cur = attr._attr; + + while (cur->prev_attribute_c->next_attribute) cur = cur->prev_attribute_c; + + if (cur != _root->first_attribute) return xml_attribute(); + + xml_attribute a(allocate_attribute(get_allocator(_root))); + if (!a) return xml_attribute(); + + a.set_name(name); + + if (attr._attr->prev_attribute_c->next_attribute) + attr._attr->prev_attribute_c->next_attribute = a._attr; + else + _root->first_attribute = a._attr; + + a._attr->prev_attribute_c = attr._attr->prev_attribute_c; + a._attr->next_attribute = attr._attr; + attr._attr->prev_attribute_c = a._attr; + + return a; + } + + xml_attribute xml_node::insert_attribute_after(const char_t* name, const xml_attribute& attr) + { + if ((type() != node_element && type() != node_declaration) || attr.empty()) return xml_attribute(); + + // check that attribute belongs to *this + xml_attribute_struct* cur = attr._attr; + + while (cur->prev_attribute_c->next_attribute) cur = cur->prev_attribute_c; + + if (cur != _root->first_attribute) return xml_attribute(); + + xml_attribute a(allocate_attribute(get_allocator(_root))); + if (!a) return xml_attribute(); + + a.set_name(name); + + if (attr._attr->next_attribute) + attr._attr->next_attribute->prev_attribute_c = a._attr; + else + _root->first_attribute->prev_attribute_c = a._attr; + + a._attr->next_attribute = attr._attr->next_attribute; + a._attr->prev_attribute_c = attr._attr; + attr._attr->next_attribute = a._attr; + + return a; + } + + xml_attribute xml_node::append_copy(const xml_attribute& proto) + { + if (!proto) return xml_attribute(); + + xml_attribute result = append_attribute(proto.name()); + result.set_value(proto.value()); + + return result; + } + + xml_attribute xml_node::prepend_copy(const xml_attribute& proto) + { + if (!proto) return xml_attribute(); + + xml_attribute result = prepend_attribute(proto.name()); + result.set_value(proto.value()); + + return result; + } + + xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr) + { + if (!proto) return xml_attribute(); + + xml_attribute result = insert_attribute_after(proto.name(), attr); + result.set_value(proto.value()); + + return result; + } + + xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr) + { + if (!proto) return xml_attribute(); + + xml_attribute result = insert_attribute_before(proto.name(), attr); + result.set_value(proto.value()); + + return result; + } + + xml_node xml_node::append_child(xml_node_type type) + { + if (!allow_insert_child(this->type(), type)) return xml_node(); + + xml_node n(append_node(_root, get_allocator(_root), type)); + + if (type == node_declaration) n.set_name(PUGIXML_TEXT("xml")); + + return n; + } + + xml_node xml_node::prepend_child(xml_node_type type) + { + if (!allow_insert_child(this->type(), type)) return xml_node(); + + xml_node n(allocate_node(get_allocator(_root), type)); + if (!n) return xml_node(); + + n._root->parent = _root; + + xml_node_struct* head = _root->first_child; + + if (head) + { + n._root->prev_sibling_c = head->prev_sibling_c; + head->prev_sibling_c = n._root; + } + else + n._root->prev_sibling_c = n._root; + + n._root->next_sibling = head; + _root->first_child = n._root; + + if (type == node_declaration) n.set_name(PUGIXML_TEXT("xml")); + + return n; + } + + xml_node xml_node::insert_child_before(xml_node_type type, const xml_node& node) + { + if (!allow_insert_child(this->type(), type)) return xml_node(); + if (!node._root || node._root->parent != _root) return xml_node(); + + xml_node n(allocate_node(get_allocator(_root), type)); + if (!n) return xml_node(); + + n._root->parent = _root; + + if (node._root->prev_sibling_c->next_sibling) + node._root->prev_sibling_c->next_sibling = n._root; + else + _root->first_child = n._root; + + n._root->prev_sibling_c = node._root->prev_sibling_c; + n._root->next_sibling = node._root; + node._root->prev_sibling_c = n._root; + + if (type == node_declaration) n.set_name(PUGIXML_TEXT("xml")); + + return n; + } + + xml_node xml_node::insert_child_after(xml_node_type type, const xml_node& node) + { + if (!allow_insert_child(this->type(), type)) return xml_node(); + if (!node._root || node._root->parent != _root) return xml_node(); + + xml_node n(allocate_node(get_allocator(_root), type)); + if (!n) return xml_node(); + + n._root->parent = _root; + + if (node._root->next_sibling) + node._root->next_sibling->prev_sibling_c = n._root; + else + _root->first_child->prev_sibling_c = n._root; + + n._root->next_sibling = node._root->next_sibling; + n._root->prev_sibling_c = node._root; + node._root->next_sibling = n._root; + + if (type == node_declaration) n.set_name(PUGIXML_TEXT("xml")); + + return n; + } + + xml_node xml_node::append_child(const char_t* name) + { + xml_node result = append_child(node_element); + + result.set_name(name); + + return result; + } + + xml_node xml_node::prepend_child(const char_t* name) + { + xml_node result = prepend_child(node_element); + + result.set_name(name); + + return result; + } + + 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; + } + + 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; + } + + xml_node xml_node::append_copy(const xml_node& proto) + { + xml_node result = append_child(proto.type()); + + if (result) recursive_copy_skip(result, proto, result); + + return result; + } + + xml_node xml_node::prepend_copy(const xml_node& proto) + { + xml_node result = prepend_child(proto.type()); + + if (result) recursive_copy_skip(result, proto, result); + + return result; + } + + xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node) + { + xml_node result = insert_child_after(proto.type(), node); + + if (result) recursive_copy_skip(result, proto, result); + + return result; + } + + xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node) + { + xml_node result = insert_child_before(proto.type(), node); + + if (result) recursive_copy_skip(result, proto, result); + + return result; + } + + bool xml_node::remove_attribute(const char_t* name) + { + return remove_attribute(attribute(name)); + } + + bool xml_node::remove_attribute(const xml_attribute& a) + { + if (!_root || !a._attr) return false; + + // check that attribute belongs to *this + xml_attribute_struct* attr = a._attr; + + while (attr->prev_attribute_c->next_attribute) attr = attr->prev_attribute_c; + + if (attr != _root->first_attribute) return false; + + if (a._attr->next_attribute) a._attr->next_attribute->prev_attribute_c = a._attr->prev_attribute_c; + else if (_root->first_attribute) _root->first_attribute->prev_attribute_c = a._attr->prev_attribute_c; + + if (a._attr->prev_attribute_c->next_attribute) a._attr->prev_attribute_c->next_attribute = a._attr->next_attribute; + else _root->first_attribute = a._attr->next_attribute; + + destroy_attribute(a._attr, get_allocator(_root)); + + return true; + } + + bool xml_node::remove_child(const char_t* name) + { + return remove_child(child(name)); + } + + bool xml_node::remove_child(const xml_node& n) + { + if (!_root || !n._root || n._root->parent != _root) return false; + + if (n._root->next_sibling) n._root->next_sibling->prev_sibling_c = n._root->prev_sibling_c; + else if (_root->first_child) _root->first_child->prev_sibling_c = n._root->prev_sibling_c; + + if (n._root->prev_sibling_c->next_sibling) n._root->prev_sibling_c->next_sibling = n._root->next_sibling; + else _root->first_child = n._root->next_sibling; + + destroy_node(n._root, get_allocator(_root)); + + return true; + } + + 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 && strequal(name, i->name)) + { + for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) + if (strequal(attr_name, a->name) && strequal(attr_value, a->value)) + return xml_node(i); + } + + return xml_node(); + } + + 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 (strequal(attr_name, a->name) && strequal(attr_value, a->value)) + return xml_node(i); + + return xml_node(); + } + +#ifndef PUGIXML_NO_STL + string_t xml_node::path(char_t delimiter) const + { + string_t path; + + xml_node cursor = *this; // Make a copy. + + path = cursor.name(); + + while (cursor.parent()) + { + cursor = cursor.parent(); + + string_t temp = cursor.name(); + temp += delimiter; + temp += path; + path.swap(temp); + } + + return path; + } +#endif + + 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 || !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 && 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(); + } + } + + bool xml_node::traverse(xml_tree_walker& walker) + { + walker._depth = -1; + + xml_node arg_begin = *this; + if (!walker.begin(arg_begin)) return false; + + xml_node cur = first_child(); + + 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 + { + // Borland C++ workaround + while (!cur.next_sibling() && cur != *this && (bool)cur.parent()) + { + --walker._depth; + cur = cur.parent(); + } + + if (cur != *this) + cur = cur.next_sibling(); + } + } + while (cur && cur != *this); + } + + assert(walker._depth == -1); + + xml_node arg_end = *this; + return walker.end(arg_end); + } + + size_t xml_node::hash_value() const + { + return static_cast(reinterpret_cast(_root) / sizeof(xml_node_struct)); + } + + xml_node_struct* xml_node::internal_object() const + { + return _root; + } + + void xml_node::print(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const + { + if (!_root) return; + + xml_buffered_writer buffered_writer(writer, encoding); + + node_output(buffered_writer, *this, indent, flags, depth); + } + +#ifndef PUGIXML_NO_STL + 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); + } + + 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 + + ptrdiff_t xml_node::offset_debug() const + { + xml_node_struct* r = root()._root; + + if (!r) return -1; + + const char_t* buffer = static_cast(r)->buffer; + + if (!buffer) return -1; + + switch (type()) + { + case node_document: + return 0; + + case node_element: + case node_declaration: + case node_pi: + return (_root->header & xml_memory_page_name_allocated_mask) ? -1 : _root->name - buffer; + + case node_pcdata: + case node_cdata: + case node_comment: + case node_doctype: + return (_root->header & xml_memory_page_value_allocated_mask) ? -1 : _root->value - buffer; + + default: + return -1; + } + } + +#ifdef __BORLANDC__ + bool operator&&(const xml_node& lhs, bool rhs) + { + return (bool)lhs && rhs; + } + + bool operator||(const xml_node& lhs, bool rhs) + { + return (bool)lhs || rhs; + } +#endif + + xml_node_iterator::xml_node_iterator() + { + } + + xml_node_iterator::xml_node_iterator(const xml_node& node): _wrap(node), _parent(node.parent()) + { + } + + xml_node_iterator::xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) + { + } + + bool xml_node_iterator::operator==(const xml_node_iterator& rhs) const + { + return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; + } + + bool xml_node_iterator::operator!=(const xml_node_iterator& rhs) const + { + return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; + } + + xml_node& xml_node_iterator::operator*() + { + assert(_wrap._root); + return _wrap; + } + + xml_node* xml_node_iterator::operator->() + { + assert(_wrap._root); + return &_wrap; + } + + const xml_node_iterator& xml_node_iterator::operator++() + { + assert(_wrap._root); + _wrap._root = _wrap._root->next_sibling; + return *this; + } + + xml_node_iterator xml_node_iterator::operator++(int) + { + xml_node_iterator temp = *this; + ++*this; + return temp; + } + + const xml_node_iterator& xml_node_iterator::operator--() + { + _wrap = _wrap._root ? _wrap.previous_sibling() : _parent.last_child(); + return *this; + } + + xml_node_iterator xml_node_iterator::operator--(int) + { + xml_node_iterator temp = *this; + --*this; + return temp; + } + + xml_attribute_iterator::xml_attribute_iterator() + { + } + + xml_attribute_iterator::xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent): _wrap(attr), _parent(parent) + { + } + + xml_attribute_iterator::xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) + { + } + + bool xml_attribute_iterator::operator==(const xml_attribute_iterator& rhs) const + { + return _wrap._attr == rhs._wrap._attr && _parent._root == rhs._parent._root; + } + + bool xml_attribute_iterator::operator!=(const xml_attribute_iterator& rhs) const + { + return _wrap._attr != rhs._wrap._attr || _parent._root != rhs._parent._root; + } + + xml_attribute& xml_attribute_iterator::operator*() + { + assert(_wrap._attr); + return _wrap; + } + + xml_attribute* xml_attribute_iterator::operator->() + { + assert(_wrap._attr); + return &_wrap; + } + + const xml_attribute_iterator& xml_attribute_iterator::operator++() + { + assert(_wrap._attr); + _wrap._attr = _wrap._attr->next_attribute; + return *this; + } + + xml_attribute_iterator xml_attribute_iterator::operator++(int) + { + xml_attribute_iterator temp = *this; + ++*this; + return temp; + } + + const xml_attribute_iterator& xml_attribute_iterator::operator--() + { + _wrap = _wrap._attr ? _wrap.previous_attribute() : _parent.last_attribute(); + return *this; + } + + xml_attribute_iterator xml_attribute_iterator::operator--(int) + { + xml_attribute_iterator temp = *this; + --*this; + return temp; + } + + xml_parse_result::xml_parse_result(): status(status_internal_error), offset(0), encoding(encoding_auto) + { + } + + xml_parse_result::operator bool() const + { + return status == status_ok; + } + + 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"; + + default: return "Unknown error"; + } + } + + xml_document::xml_document(): _buffer(0) + { + create(); + } + + xml_document::~xml_document() + { + destroy(); + } + + void xml_document::reset() + { + destroy(); + create(); + } + + void xml_document::reset(const xml_document& proto) + { + reset(); + + for (xml_node cur = proto.first_child(); cur; cur = cur.next_sibling()) + append_copy(cur); + } + + void xml_document::create() + { + // initialize sentinel page + STATIC_ASSERT(offsetof(xml_memory_page, data) + sizeof(xml_document_struct) + xml_memory_page_alignment <= sizeof(_memory)); + + // align upwards to page boundary + void* page_memory = reinterpret_cast((reinterpret_cast(_memory) + (xml_memory_page_alignment - 1)) & ~(xml_memory_page_alignment - 1)); + + // prepare page structure + xml_memory_page* page = xml_memory_page::construct(page_memory); + + page->busy_size = xml_memory_page_size; + + // allocate new root + _root = new (page->data) xml_document_struct(page); + _root->prev_sibling_c = _root; + + // setup sentinel page + page->allocator = static_cast(_root); + } + + void xml_document::destroy() + { + // destroy static storage + if (_buffer) + { + global_deallocate(_buffer); + _buffer = 0; + } + + // destroy dynamic storage, leave sentinel page (it's in static memory) + if (_root) + { + xml_memory_page* root_page = reinterpret_cast(_root->header & xml_memory_page_pointer_mask); + assert(root_page && !root_page->prev && !root_page->memory); + + // destroy all pages + for (xml_memory_page* page = root_page->next; page; ) + { + xml_memory_page* next = page->next; + + xml_allocator::deallocate_page(page); + + page = next; + } + + // cleanup root page + root_page->allocator = 0; + root_page->next = 0; + root_page->busy_size = root_page->freed_size = 0; + + _root = 0; + } + } + +#ifndef PUGIXML_NO_STL + xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options, xml_encoding encoding) + { + reset(); + + return load_stream_impl(*this, stream, options, encoding); + } + + xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options) + { + reset(); + + return load_stream_impl(*this, stream, options, encoding_wchar); + } +#endif + + xml_parse_result xml_document::load(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, strlength(contents) * sizeof(char_t), options, encoding); + } + + xml_parse_result xml_document::load_file(const char* path, unsigned int options, xml_encoding encoding) + { + reset(); + + FILE* file = fopen(path, "rb"); + + return load_file_impl(*this, file, options, encoding); + } + + xml_parse_result xml_document::load_file(const wchar_t* path, unsigned int options, xml_encoding encoding) + { + reset(); + + FILE* file = open_file_wide(path, L"rb"); + + return load_file_impl(*this, file, options, encoding); + } + + xml_parse_result xml_document::load_buffer_impl(void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own) + { + reset(); + + // check input buffer + assert(contents || size == 0); + + // get actual encoding + xml_encoding buffer_encoding = get_buffer_encoding(encoding, contents, size); + + // get private buffer + char_t* buffer = 0; + size_t length = 0; + + if (!convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return make_parse_result(status_out_of_memory); + + // delete original buffer if we performed a conversion + if (own && buffer != contents && contents) global_deallocate(contents); + + // parse + xml_parse_result res = xml_parser::parse(buffer, length, _root, options); + + // remember encoding + res.encoding = buffer_encoding; + + // grab onto buffer if it's our buffer, user is responsible for deallocating contens himself + if (own || buffer != contents) _buffer = buffer; + + return res; + } + + xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) + { + return load_buffer_impl(const_cast(contents), size, options, encoding, false, false); + } + + xml_parse_result xml_document::load_buffer_inplace(void* contents, size_t size, unsigned int options, xml_encoding encoding) + { + return load_buffer_impl(contents, size, options, encoding, true, false); + } + + xml_parse_result xml_document::load_buffer_inplace_own(void* contents, size_t size, unsigned int options, xml_encoding encoding) + { + return load_buffer_impl(contents, size, options, encoding, true, true); + } + + void xml_document::save(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding) const + { + if (flags & format_write_bom) write_bom(writer, get_write_encoding(encoding)); + + xml_buffered_writer buffered_writer(writer, encoding); + + if (!(flags & format_no_declaration) && !has_declaration(*this)) + { + buffered_writer.write(PUGIXML_TEXT("")); + if (!(flags & format_raw)) buffered_writer.write('\n'); + } + + node_output(buffered_writer, *this, indent, flags, 0); + } + +#ifndef PUGIXML_NO_STL + 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); + } + + 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 + + bool xml_document::save_file(const char* path, const char_t* indent, unsigned int flags, xml_encoding encoding) const + { + FILE* file = fopen(path, "wb"); + if (!file) return false; + + xml_writer_file writer(file); + save(writer, indent, flags, encoding); + + fclose(file); + + return true; + } + + bool xml_document::save_file(const wchar_t* path, const char_t* indent, unsigned int flags, xml_encoding encoding) const + { + FILE* file = open_file_wide(path, L"wb"); + if (!file) return false; + + xml_writer_file writer(file); + save(writer, indent, flags, encoding); + + fclose(file); + + return true; + } + + xml_node xml_document::document_element() const + { + for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) + if ((i->header & xml_memory_page_type_mask) + 1 == node_element) + return xml_node(i); + + return xml_node(); + } + +#ifndef PUGIXML_NO_STL + std::string PUGIXML_FUNCTION as_utf8(const wchar_t* str) + { + assert(str); + + return as_utf8_impl(str, wcslen(str)); + } + + std::string PUGIXML_FUNCTION as_utf8(const std::wstring& str) + { + return as_utf8_impl(str.c_str(), str.size()); + } + + std::wstring PUGIXML_FUNCTION as_wide(const char* str) + { + assert(str); + + return as_wide_impl(str, strlen(str)); + } + + std::wstring PUGIXML_FUNCTION as_wide(const std::string& str) + { + return as_wide_impl(str.c_str(), str.size()); + } +#endif + + void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate) + { + global_allocate = allocate; + global_deallocate = deallocate; + } + + allocation_function PUGIXML_FUNCTION get_memory_allocation_function() + { + return global_allocate; + } + + deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function() + { + return global_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) + std::bidirectional_iterator_tag _Iter_cat(const xml_node_iterator&) + { + return std::bidirectional_iterator_tag(); + } + + std::bidirectional_iterator_tag _Iter_cat(const xml_attribute_iterator&) + { + return std::bidirectional_iterator_tag(); + } +} +#endif + +#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) +namespace std +{ + // Workarounds for (non-standard) iterator category detection + std::bidirectional_iterator_tag __iterator_category(const xml_node_iterator&) + { + return std::bidirectional_iterator_tag(); + } + + std::bidirectional_iterator_tag __iterator_category(const xml_attribute_iterator&) + { + return std::bidirectional_iterator_tag(); + } +} +#endif + +#ifndef PUGIXML_NO_XPATH + +// STL replacements +namespace +{ + 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 void swap(T& lhs, T& rhs) + { + T temp = lhs; + lhs = rhs; + rhs = temp; + } + + template 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 void reverse(I begin, I end) + { + while (begin + 1 < end) swap(*begin++, *--end); + } + + template I unique(I begin, I end) + { + // fast skip head + while (begin + 1 < end && *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 void copy_backwards(I begin, I end, I target) + { + while (begin != end) *--target = *--end; + } + + template void insertion_sort(I begin, I end, const Pred& pred, T*) + { + assert(begin != end); + + for (I it = begin + 1; it != end; ++it) + { + T val = *it; + + if (pred(val, *begin)) + { + // move to front + copy_backwards(begin, it, it + 1); + *begin = val; + } + else + { + I hole = it; + + // move hole backwards + while (pred(val, *(hole - 1))) + { + *hole = *(hole - 1); + hole--; + } + + // fill hole with element + *hole = val; + } + } + } + + // std variant for elements with == + template void partition(I begin, I middle, I end, const Pred& pred, I* out_eqbeg, I* out_eqend) + { + I eqbeg = middle, eqend = middle + 1; + + // expand equal range + while (eqbeg != begin && *(eqbeg - 1) == *eqbeg) --eqbeg; + while (eqend != end && *eqend == *eqbeg) ++eqend; + + // process outer elements + I ltend = eqbeg, gtbeg = eqend; + + for (;;) + { + // find the element from the right side that belongs to the left one + for (; gtbeg != end; ++gtbeg) + if (!pred(*eqbeg, *gtbeg)) + { + if (*gtbeg == *eqbeg) swap(*gtbeg, *eqend++); + else break; + } + + // find the element from the left side that belongs to the right one + for (; ltend != begin; --ltend) + if (!pred(*(ltend - 1), *eqbeg)) + { + if (*eqbeg == *(ltend - 1)) swap(*(ltend - 1), *--eqbeg); + else break; + } + + // scanned all elements + if (gtbeg == end && ltend == begin) + { + *out_eqbeg = eqbeg; + *out_eqend = eqend; + return; + } + + // make room for elements by moving equal area + if (gtbeg == end) + { + if (--ltend != --eqbeg) swap(*ltend, *eqbeg); + swap(*eqbeg, *--eqend); + } + else if (ltend == begin) + { + if (eqend != gtbeg) swap(*eqbeg, *eqend); + ++eqend; + swap(*gtbeg++, *eqbeg++); + } + else swap(*gtbeg++, *--ltend); + } + } + + template void 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); + } + + template void median(I first, I middle, I last, const Pred& pred) + { + if (last - first <= 40) + { + // median of three for small chunks + median3(first, middle, last, pred); + } + else + { + // median of nine + size_t step = (last - first + 1) / 8; + + median3(first, first + step, first + 2 * step, pred); + median3(middle - step, middle, middle + step, pred); + median3(last - 2 * step, last - step, last, pred); + median3(first + step, middle, last - step, pred); + } + } + + template void sort(I begin, I end, const Pred& pred) + { + // sort large chunks + while (end - begin > 32) + { + // find median element + I middle = begin + (end - begin) / 2; + median(begin, middle, end - 1, pred); + + // partition in three chunks (< = >) + I eqbeg, eqend; + partition(begin, middle, end, 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 + if (begin != end) insertion_sort(begin, end, pred, &*begin); + } +} + +// Allocator used for AST and evaluation stacks +namespace +{ + struct xpath_memory_block + { + xpath_memory_block* next; + + char data[4096]; + }; + + class xpath_allocator + { + xpath_memory_block* _root; + size_t _root_size; + + public: +#ifdef PUGIXML_NO_EXCEPTIONS + jmp_buf* error_handler; +#endif + + xpath_allocator(xpath_memory_block* root, size_t root_size = 0): _root(root), _root_size(root_size) + { +#ifdef PUGIXML_NO_EXCEPTIONS + error_handler = 0; +#endif + } + + void* allocate_nothrow(size_t size) + { + const size_t block_capacity = sizeof(_root->data); + + // align size so that we're able to store pointers in subsequent blocks + size = (size + sizeof(void*) - 1) & ~(sizeof(void*) - 1); + + if (_root_size + size <= block_capacity) + { + void* buf = _root->data + _root_size; + _root_size += size; + return buf; + } + else + { + size_t block_data_size = (size > block_capacity) ? size : block_capacity; + size_t block_size = block_data_size + offsetof(xpath_memory_block, data); + + xpath_memory_block* block = static_cast(global_allocate(block_size)); + if (!block) return 0; + + block->next = _root; + + _root = block; + _root_size = size; + + return block->data; + } + } + + void* allocate(size_t size) + { + void* result = allocate_nothrow(size); + + if (!result) + { +#ifdef PUGIXML_NO_EXCEPTIONS + assert(error_handler); + longjmp(*error_handler, 1); +#else + throw std::bad_alloc(); +#endif + } + + return result; + } + + void* reallocate(void* ptr, size_t old_size, size_t new_size) + { + // align size so that we're able to store pointers in subsequent blocks + old_size = (old_size + sizeof(void*) - 1) & ~(sizeof(void*) - 1); + new_size = (new_size + sizeof(void*) - 1) & ~(sizeof(void*) - 1); + + // we can only reallocate the last object + assert(ptr == 0 || static_cast(ptr) + old_size == _root->data + _root_size); + + // adjust root size so that we have not allocated the object at all + bool only_object = (_root_size == old_size); + + if (ptr) _root_size -= old_size; + + // allocate a new version (this will obviously reuse the memory if possible) + void* result = allocate(new_size); + assert(result); + + // we have a new block + if (result != ptr && ptr) + { + // copy old data + assert(new_size > old_size); + memcpy(result, ptr, old_size); + + // free the previous page if it had no other objects + if (only_object) + { + assert(_root->data == result); + assert(_root->next); + + xpath_memory_block* next = _root->next->next; + + if (next) + { + // deallocate the whole page, unless it was the first one + global_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; + + global_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; + + global_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; + +#ifdef PUGIXML_NO_EXCEPTIONS + jmp_buf error_handler; +#endif + + xpath_stack_data(): result(blocks + 0), temp(blocks + 1) + { + blocks[0].next = blocks[1].next = 0; + + stack.result = &result; + stack.temp = &temp; + +#ifdef PUGIXML_NO_EXCEPTIONS + result.error_handler = temp.error_handler = &error_handler; +#endif + } + + ~xpath_stack_data() + { + result.release(); + temp.release(); + } + }; +} + +// String class +namespace +{ + class xpath_string + { + const char_t* _buffer; + bool _uses_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))); + assert(result); + + memcpy(result, string, length * sizeof(char_t)); + result[length] = 0; + + return result; + } + + static char_t* duplicate_string(const char_t* string, xpath_allocator* alloc) + { + return duplicate_string(string, strlength(string), alloc); + } + + public: + xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false) + { + } + + explicit xpath_string(const char_t* str, xpath_allocator* alloc) + { + bool empty = (*str == 0); + + _buffer = empty ? PUGIXML_TEXT("") : duplicate_string(str, alloc); + _uses_heap = !empty; + } + + explicit xpath_string(const char_t* str, bool use_heap): _buffer(str), _uses_heap(use_heap) + { + } + + xpath_string(const char_t* begin, const char_t* end, xpath_allocator* alloc) + { + assert(begin <= end); + + bool empty = (begin == end); + + _buffer = empty ? PUGIXML_TEXT("") : duplicate_string(begin, static_cast(end - begin), alloc); + _uses_heap = !empty; + } + + 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 = strlength(_buffer); + size_t source_length = strlength(o._buffer); + size_t 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), (length + 1) * sizeof(char_t))); + assert(result); + + // 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[length] = 0; + + // finalize + _buffer = result; + _uses_heap = true; + } + } + + const char_t* c_str() const + { + return _buffer; + } + + size_t length() const + { + return strlength(_buffer); + } + + char_t* data(xpath_allocator* alloc) + { + // make private heap copy + if (!_uses_heap) + { + _buffer = duplicate_string(_buffer, alloc); + _uses_heap = true; + } + + 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; + } + }; + + xpath_string xpath_string_const(const char_t* str) + { + return xpath_string(str, false); + } +} + +namespace +{ + bool starts_with(const char_t* string, const char_t* pattern) + { + while (*pattern && *string == *pattern) + { + string++; + pattern++; + } + + return *pattern == 0; + } + + 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 + } + + 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 + char_t tolower_ascii(char_t ch) + { + return static_cast(ch - 'A') < 26 ? static_cast(ch | ' ') : ch; + } + + xpath_string string_value(const xpath_node& na, xpath_allocator* alloc) + { + if (na.attribute()) + return xpath_string_const(na.attribute().value()); + else + { + const xml_node& n = na.node(); + + switch (n.type()) + { + case node_pcdata: + case node_cdata: + case node_comment: + case node_pi: + return xpath_string_const(n.value()); + + case node_document: + case node_element: + { + xpath_string result; + + xml_node cur = n.first_child(); + + while (cur && cur != n) + { + if (cur.type() == node_pcdata || cur.type() == node_cdata) + result.append(xpath_string_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(); + } + } + } + + unsigned int node_height(xml_node n) + { + unsigned int result = 0; + + while (n) + { + ++result; + n = n.parent(); + } + + return result; + } + + bool node_is_before(xml_node ln, unsigned int lh, xml_node rn, unsigned int rh) + { + // normalize heights + for (unsigned int i = rh; i < lh; i++) ln = ln.parent(); + for (unsigned int j = lh; j < rh; j++) rn = rn.parent(); + + // one node is the ancestor of the other + if (ln == rn) return lh < rh; + + // find common ancestor + while (ln.parent() != rn.parent()) + { + ln = ln.parent(); + rn = 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 + for (; ln; ln = ln.next_sibling()) + if (ln == rn) + return true; + + return false; + } + + bool node_is_ancestor(xml_node parent, xml_node node) + { + while (node && node != parent) node = node.parent(); + + return parent && node == parent; + } + + const void* document_order(const xpath_node& xnode) + { + xml_node_struct* node = xnode.node().internal_object(); + + if (node) + { + if (node->name && (node->header & xml_memory_page_name_allocated_mask) == 0) return node->name; + if (node->value && (node->header & xml_memory_page_value_allocated_mask) == 0) return node->value; + return 0; + } + + xml_attribute_struct* attr = xnode.attribute().internal_object(); + + if (attr) + { + if ((attr->header & xml_memory_page_name_allocated_mask) == 0) return attr->name; + if ((attr->header & xml_memory_page_value_allocated_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_order(lhs); + const void* ro = document_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; + + unsigned int lh = node_height(ln); + unsigned int rh = node_height(rn); + + return node_is_before(ln, lh, rn, rh); + } + }; + + struct duplicate_comparator + { + bool operator()(const xpath_node& lhs, const xpath_node& rhs) const + { + if (lhs.attribute()) return rhs.attribute() ? lhs.attribute() < rhs.attribute() : true; + else return rhs.attribute() ? false : lhs.node() < rhs.node(); + } + }; + + double gen_nan() + { +#if defined(__STDC_IEC_559__) || ((FLT_RADIX - 0 == 2) && (FLT_MAX_EXP - 0 == 128) && (FLT_MANT_DIG - 0 == 24)) + union { float f; int32_t i; } u[sizeof(float) == sizeof(int32_t) ? 1 : -1]; + u[0].i = 0x7fc00000; + return u[0].f; +#else + // fallback + const volatile double zero = 0.0; + return zero / zero; +#endif + } + + bool is_nan(double value) + { +#if defined(_MSC_VER) || 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 + } + + const char_t* convert_number_to_string_special(double value) + { +#if defined(_MSC_VER) || defined(__BORLANDC__) + if (_finite(value)) return (value == 0) ? PUGIXML_TEXT("0") : 0; + if (_isnan(value)) return PUGIXML_TEXT("NaN"); + return PUGIXML_TEXT("-Infinity") + (value > 0); +#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 PUGIXML_TEXT("-Infinity") + (value > 0); + + 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 PUGIXML_TEXT("-Infinity") + (value > 0); + return 0; +#endif + } + + bool convert_number_to_boolean(double value) + { + return (value != 0 && !is_nan(value)); + } + + 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(_MSC_VER) && _MSC_VER >= 1400 + void convert_number_to_mantissa_exponent(double value, char* buffer, size_t buffer_size, char** out_mantissa, int* out_exponent) + { + // get base values + int sign, exponent; + _ecvt_s(buffer, buffer_size, value, DBL_DIG + 1, &exponent, &sign); + + // truncate redundant zeros + truncate_zeros(buffer, buffer + strlen(buffer)); + + // fill results + *out_mantissa = buffer; + *out_exponent = exponent; + } +#else + void convert_number_to_mantissa_exponent(double value, char* buffer, size_t buffer_size, char** out_mantissa, int* out_exponent) + { + // get a scientific notation value with IEEE DBL_DIG decimals + sprintf(buffer, "%.*e", DBL_DIG, value); + assert(strlen(buffer) < buffer_size); + (void)!buffer_size; + + // 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 + + 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_const(special); + + // get mantissa + exponent form + char mantissa_buffer[64]; + + char* mantissa; + int exponent; + convert_number_to_mantissa_exponent(value, mantissa_buffer, sizeof(mantissa_buffer), &mantissa, &exponent); + + // make the number! + char_t result[512]; + char_t* s = result; + + // sign + if (value < 0) *s++ = '-'; + + // integer part + if (exponent <= 0) + { + *s++ = '0'; + } + else + { + while (exponent > 0) + { + assert(*mantissa == 0 || (unsigned)(*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((unsigned)(*mantissa - '0') <= 9); + *s++ = *mantissa++; + } + } + + // zero-terminate + assert(s < result + sizeof(result) / sizeof(result[0])); + *s = 0; + + return xpath_string(result, alloc); + } + + bool check_string_to_number_format(const char_t* string) + { + // parse leading whitespace + while (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 (!IS_CHARTYPEX(string[0], ctx_digit) && (string[0] != '.' || !IS_CHARTYPEX(string[1], ctx_digit))) return false; + + // parse integer part + while (IS_CHARTYPEX(*string, ctx_digit)) ++string; + + // parse decimal part + if (*string == '.') + { + ++string; + + while (IS_CHARTYPEX(*string, ctx_digit)) ++string; + } + + // parse trailing whitespace + while (IS_CHARTYPE(*string, ct_space)) ++string; + + return *string == 0; + } + + 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 atof(string); +#endif + } + + bool convert_string_to_number(const char_t* begin, const char_t* end, double* out_result) + { + char_t buffer[32]; + + 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(global_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) global_deallocate(scratch); + + return true; + } + + double round_nearest(double value) + { + return floor(value + 0.5); + } + + 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); + } + + const char_t* qualified_name(const xpath_node& node) + { + return node.attribute() ? node.attribute().name() : node.node().name(); + } + + 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()(const 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; + } + }; + + const char_t* namespace_uri(const 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(""); + } + + const char_t* namespace_uri(const xml_attribute& attr, const 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(""); + } + + const char_t* namespace_uri(const xpath_node& node) + { + return node.attribute() ? namespace_uri(node.attribute(), node.parent()) : namespace_uri(node.node()); + } + + void normalize_space(char_t* buffer) + { + char_t* write = buffer; + + for (char_t* it = buffer; *it; ) + { + char_t ch = *it++; + + if (IS_CHARTYPE(ch, ct_space)) + { + // replace whitespace sequence with single space + while (IS_CHARTYPE(*it, ct_space)) it++; + + // avoid leading spaces + if (write != buffer) *write++ = ' '; + } + else *write++ = ch; + } + + // remove trailing space + if (write != buffer && IS_CHARTYPE(write[-1], ct_space)) write--; + + // zero-terminate + *write = 0; + } + + void translate(char_t* buffer, const char_t* from, const char_t* to) + { + size_t to_length = strlength(to); + + char_t* write = buffer; + + while (*buffer) + { + 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; + } + + struct xpath_variable_boolean: xpath_variable + { + xpath_variable_boolean(): value(false) + { + } + + bool value; + char_t name[1]; + }; + + struct xpath_variable_number: xpath_variable + { + xpath_variable_number(): value(0) + { + } + + double value; + char_t name[1]; + }; + + struct xpath_variable_string: xpath_variable + { + xpath_variable_string(): value(0) + { + } + + ~xpath_variable_string() + { + if (value) global_deallocate(value); + } + + char_t* value; + char_t name[1]; + }; + + struct xpath_variable_node_set: xpath_variable + { + xpath_node_set value; + char_t name[1]; + }; + + const xpath_node_set dummy_node_set; + + 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 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 = global_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; + } + + 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 void delete_xpath_variable(T* var) + { + var->~T(); + global_deallocate(var); + } + + 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(!"Invalid variable type"); + } + } + + xpath_variable* get_variable(xpath_variable_set* set, const char_t* begin, const char_t* end) + { + char_t buffer[32]; + + 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(global_allocate((length + 1) * sizeof(char_t))); + if (!scratch) return 0; + } + + // copy string to zero-terminated buffer and perform lookup + memcpy(scratch, begin, length * sizeof(char_t)); + scratch[length] = 0; + + xpath_variable* result = set->get(scratch); + + // free dummy buffer + if (scratch != buffer) global_deallocate(scratch); + + return result; + } +} + +// Internal node set class +namespace +{ + 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) + { + sort(begin, end, document_order_comparator()); + + type = xpath_node_set::type_sorted; + } + + if (type != order) reverse(begin, end); + + return order; + } + + 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(!"Invalid node set type"); + 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(const xpath_node& node, xpath_allocator* alloc) + { + if (_end == _eos) + { + 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))); + assert(data); + + // finalize + _begin = data; + _end = data + capacity; + _eos = data + new_capacity; + } + + *_end++ = node; + } + + void append(const xpath_node* begin, const xpath_node* end, xpath_allocator* alloc) + { + 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))); + assert(data); + + // 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() + { + if (_type == xpath_node_set::type_unsorted) + sort(_begin, _end, duplicate_comparator()); + + _end = unique(_begin, _end); + } + + xpath_node_set::type_t type() const + { + return _type; + } + + void set_type(xpath_node_set::type_t type) + { + _type = type; + } + }; +} + +namespace +{ + 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 (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 (IS_CHARTYPEX(*cur, ctx_start_symbol)) + { + _cur_lexeme_contents.begin = cur; + + while (IS_CHARTYPEX(*cur, ctx_symbol)) cur++; + + if (cur[0] == ':' && IS_CHARTYPEX(cur[1], ctx_symbol)) // qname + { + cur++; // : + + while (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 (IS_CHARTYPEX(*(cur+1), ctx_digit)) + { + _cur_lexeme_contents.begin = cur; // . + + ++cur; + + while (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 (IS_CHARTYPEX(*cur, ctx_digit)) + { + _cur_lexeme_contents.begin = cur; + + while (IS_CHARTYPEX(*cur, ctx_digit)) cur++; + + if (*cur == '.') + { + cur++; + + while (IS_CHARTYPEX(*cur, ctx_digit)) cur++; + } + + _cur_lexeme_contents.end = cur; + + _cur_lexeme = lex_number; + } + else if (IS_CHARTYPEX(*cur, ctx_start_symbol)) + { + _cur_lexeme_contents.begin = cur; + + while (IS_CHARTYPEX(*cur, ctx_symbol)) cur++; + + if (cur[0] == ':') + { + if (cur[1] == '*') // namespace test ncname:* + { + cur += 2; // :* + } + else if (IS_CHARTYPEX(cur[1], ctx_symbol)) // namespace test qname + { + cur++; // : + + while (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_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_filter_posinv, // select * from left where right; proximity position invariant + 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 + }; + + 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 + }; + + 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 / ast_predicate + char _axis; + 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; + } _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); + xpath_node_set_raw rs = rhs->eval_node_set(c, stack); + + 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); + + 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); + + 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(!"Wrong types"); + return false; + } + + 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); + xpath_node_set_raw rs = rhs->eval_node_set(c, stack); + + 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); + + 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); + 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(!"Wrong types"); + return false; + } + } + + void apply_predicate(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack) + { + assert(ns.size() >= first); + + 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->rettype() == xpath_type_number) + { + if (expr->eval_number(c, stack) == i) + *last++ = *it; + } + else if (expr->eval_boolean(c, stack)) + *last++ = *it; + } + + ns.truncate(last); + } + + void apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack) + { + if (ns.size() == first) return; + + for (xpath_ast_node* pred = _right; pred; pred = pred->_next) + { + apply_predicate(ns, first, pred->_left, stack); + } + } + + void step_push(xpath_node_set_raw& ns, const xml_attribute& a, const xml_node& parent, xpath_allocator* alloc) + { + if (!a) return; + + const char_t* name = a.name(); + + // There are no attribute nodes corresponding to attributes that declare namespaces + // That is, "xmlns:..." or "xmlns" + if (starts_with(name, PUGIXML_TEXT("xmlns")) && (name[5] == 0 || name[5] == ':')) return; + + switch (_test) + { + case nodetest_name: + if (strequal(name, _data.nodetest)) ns.push_back(xpath_node(a, parent), alloc); + break; + + case nodetest_type_node: + case nodetest_all: + ns.push_back(xpath_node(a, parent), alloc); + break; + + case nodetest_all_in_namespace: + if (starts_with(name, _data.nodetest)) + ns.push_back(xpath_node(a, parent), alloc); + break; + + default: + ; + } + } + + void step_push(xpath_node_set_raw& ns, const xml_node& n, xpath_allocator* alloc) + { + if (!n) return; + + switch (_test) + { + case nodetest_name: + if (n.type() == node_element && strequal(n.name(), _data.nodetest)) ns.push_back(n, alloc); + break; + + case nodetest_type_node: + ns.push_back(n, alloc); + break; + + case nodetest_type_comment: + if (n.type() == node_comment) + ns.push_back(n, alloc); + break; + + case nodetest_type_text: + if (n.type() == node_pcdata || n.type() == node_cdata) + ns.push_back(n, alloc); + break; + + case nodetest_type_pi: + if (n.type() == node_pi) + ns.push_back(n, alloc); + break; + + case nodetest_pi: + if (n.type() == node_pi && strequal(n.name(), _data.nodetest)) + ns.push_back(n, alloc); + break; + + case nodetest_all: + if (n.type() == node_element) + ns.push_back(n, alloc); + break; + + case nodetest_all_in_namespace: + if (n.type() == node_element && starts_with(n.name(), _data.nodetest)) + ns.push_back(n, alloc); + break; + + default: + assert(!"Unknown axis"); + } + } + + template void step_fill(xpath_node_set_raw& ns, const xml_node& n, xpath_allocator* alloc, T) + { + const axis_t axis = T::axis; + + switch (axis) + { + case axis_attribute: + { + for (xml_attribute a = n.first_attribute(); a; a = a.next_attribute()) + step_push(ns, a, n, alloc); + + break; + } + + case axis_child: + { + for (xml_node c = n.first_child(); c; c = c.next_sibling()) + step_push(ns, c, alloc); + + break; + } + + case axis_descendant: + case axis_descendant_or_self: + { + if (axis == axis_descendant_or_self) + step_push(ns, n, alloc); + + xml_node cur = n.first_child(); + + while (cur && cur != n) + { + step_push(ns, cur, 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(); + } + } + + break; + } + + case axis_following_sibling: + { + for (xml_node c = n.next_sibling(); c; c = c.next_sibling()) + step_push(ns, c, alloc); + + break; + } + + case axis_preceding_sibling: + { + for (xml_node c = n.previous_sibling(); c; c = c.previous_sibling()) + step_push(ns, c, alloc); + + break; + } + + case axis_following: + { + xml_node cur = n; + + // exit from this node so that we don't include descendants + while (cur && !cur.next_sibling()) cur = cur.parent(); + cur = cur.next_sibling(); + + for (;;) + { + step_push(ns, cur, alloc); + + if (cur.first_child()) + cur = cur.first_child(); + else if (cur.next_sibling()) + cur = cur.next_sibling(); + else + { + while (cur && !cur.next_sibling()) cur = cur.parent(); + cur = cur.next_sibling(); + + if (!cur) break; + } + } + + break; + } + + case axis_preceding: + { + xml_node cur = n; + + while (cur && !cur.previous_sibling()) cur = cur.parent(); + cur = cur.previous_sibling(); + + for (;;) + { + if (cur.last_child()) + cur = cur.last_child(); + else + { + // leaf node, can't be ancestor + step_push(ns, cur, alloc); + + if (cur.previous_sibling()) + cur = cur.previous_sibling(); + else + { + do + { + cur = cur.parent(); + if (!cur) break; + + if (!node_is_ancestor(cur, n)) step_push(ns, cur, alloc); + } + while (!cur.previous_sibling()); + + cur = cur.previous_sibling(); + + if (!cur) break; + } + } + } + + break; + } + + case axis_ancestor: + case axis_ancestor_or_self: + { + if (axis == axis_ancestor_or_self) + step_push(ns, n, alloc); + + xml_node cur = n.parent(); + + while (cur) + { + step_push(ns, cur, alloc); + + 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(!"Unimplemented axis"); + } + } + + template void step_fill(xpath_node_set_raw& ns, const xml_attribute& a, const xml_node& p, xpath_allocator* alloc, 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 + step_push(ns, a, p, alloc); + + xml_node cur = p; + + while (cur) + { + step_push(ns, cur, alloc); + + 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 cur = p; + + for (;;) + { + if (cur.first_child()) + cur = cur.first_child(); + else if (cur.next_sibling()) + cur = cur.next_sibling(); + else + { + while (cur && !cur.next_sibling()) cur = cur.parent(); + cur = cur.next_sibling(); + + if (!cur) break; + } + + step_push(ns, cur, alloc); + } + + 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, v); + break; + } + + default: + assert(!"Unimplemented axis"); + } + } + + template xpath_node_set_raw step_do(const xpath_context& c, const xpath_stack& stack, T v) + { + const axis_t axis = T::axis; + bool 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); + + xpath_node_set_raw ns; + ns.set_type((axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling) ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted); + + if (_left) + { + xpath_node_set_raw s = _left->eval_node_set(c, stack); + + // 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); + + if (it->node()) + step_fill(ns, it->node(), stack.result, v); + else if (attributes) + step_fill(ns, it->attribute(), it->parent(), stack.result, v); + + apply_predicates(ns, size, stack); + } + } + else + { + if (c.n.node()) + step_fill(ns, c.n.node(), stack.result, v); + else if (attributes) + step_fill(ns, c.n.attribute(), c.n.parent(), stack.result, v); + + apply_predicates(ns, 0, stack); + } + + // 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(); + + return ns; + } + + public: + xpath_ast_node(ast_type_t type, xpath_value_type rettype, const char_t* value): + _type((char)type), _rettype((char)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((char)type), _rettype((char)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((char)type), _rettype((char)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((char)type), _rettype((char)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((char)type), _rettype(xpath_type_node_set), _axis((char)axis), _test((char)test), _left(left), _right(0), _next(0) + { + _data.nodetest = contents; + } + + 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_variable: + { + assert(_rettype == _data.variable->type()); + + if (_rettype == xpath_type_boolean) + return _data.variable->get_boolean(); + + // fallthrough to type conversion + } + + 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).empty(); + } + + default: + assert(!"Wrong expression for return type boolean"); + 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 (double)c.size; + + case ast_func_position: + return (double)c.position; + + case ast_func_count: + { + xpath_allocator_capture cr(stack.result); + + return (double)_left->eval_node_set(c, stack).size(); + } + + case ast_func_string_length_0: + { + xpath_allocator_capture cr(stack.result); + + return (double)string_value(c.n, stack.result).length(); + } + + case ast_func_string_length_1: + { + xpath_allocator_capture cr(stack.result); + + return (double)_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); + + 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 to type conversion + } + + 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(!"Wrong expression for return type number"); + 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++; + + // gather all strings + xpath_string static_buffer[4]; + xpath_string* buffer = static_buffer; + + // allocate on-heap for large concats + if (count > sizeof(static_buffer) / sizeof(static_buffer[0])) + { + buffer = static_cast(stack.temp->allocate(count * sizeof(xpath_string))); + assert(buffer); + } + + // 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))); + assert(result); + + 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(result, true); + } + + xpath_string eval_string(const xpath_context& c, const xpath_stack& stack) + { + switch (_type) + { + case ast_string_constant: + return xpath_string_const(_data.string); + + case ast_func_local_name_0: + { + xpath_node na = c.n; + + return xpath_string_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); + xpath_node na = ns.first(); + + return xpath_string_const(local_name(na)); + } + + case ast_func_name_0: + { + xpath_node na = c.n; + + return xpath_string_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); + xpath_node na = ns.first(); + + return xpath_string_const(qualified_name(na)); + } + + case ast_func_namespace_uri_0: + { + xpath_node na = c.n; + + return xpath_string_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); + xpath_node na = ns.first(); + + return xpath_string_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(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* result = pos + p.length(); + + return s.uses_heap() ? xpath_string(result, stack.result) : xpath_string_const(result); + } + + 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 : (size_t)first; + assert(1 <= pos && pos <= s_length + 1); + + const char_t* rbegin = s.c_str() + (pos - 1); + + return s.uses_heap() ? xpath_string(rbegin, stack.result) : xpath_string_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 : (size_t)first; + size_t end = last >= s_length + 1 ? s_length + 1 : (size_t)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_const(rbegin) : xpath_string(rbegin, rend, stack.result); + } + + case ast_func_normalize_space_0: + { + xpath_string s = string_value(c.n, stack.result); + + normalize_space(s.data(stack.result)); + + return s; + } + + case ast_func_normalize_space_1: + { + xpath_string s = _left->eval_string(c, stack); + + normalize_space(s.data(stack.result)); + + return s; + } + + 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); + + translate(s.data(stack.result), from.c_str(), to.c_str()); + + return s; + } + + case ast_variable: + { + assert(_rettype == _data.variable->type()); + + if (_rettype == xpath_type_string) + return xpath_string_const(_data.variable->get_string()); + + // fallthrough to type conversion + } + + default: + { + switch (_rettype) + { + case xpath_type_boolean: + return xpath_string_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); + return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result); + } + + default: + assert(!"Wrong expression for return type string"); + return xpath_string(); + } + } + } + } + + xpath_node_set_raw eval_node_set(const xpath_context& c, const xpath_stack& stack) + { + 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, swapped_stack); + xpath_node_set_raw rs = _right->eval_node_set(c, stack); + + // we can optimize merging two sorted sets, but this is a very rare operation, so don't bother + rs.set_type(xpath_node_set::type_unsorted); + + rs.append(ls.begin(), ls.end(), stack.result); + rs.remove_duplicates(); + + return rs; + } + + case ast_filter: + case ast_filter_posinv: + { + xpath_node_set_raw set = _left->eval_node_set(c, stack); + + // either expression is a number or it contains position() call; sort by document order + if (_type == ast_filter) set.sort_do(); + + apply_predicate(set, 0, _right, stack); + + return set; + } + + case ast_func_id: + return xpath_node_set_raw(); + + case ast_step: + { + switch (_axis) + { + case axis_ancestor: + return step_do(c, stack, axis_to_type()); + + case axis_ancestor_or_self: + return step_do(c, stack, axis_to_type()); + + case axis_attribute: + return step_do(c, stack, axis_to_type()); + + case axis_child: + return step_do(c, stack, axis_to_type()); + + case axis_descendant: + return step_do(c, stack, axis_to_type()); + + case axis_descendant_or_self: + return step_do(c, stack, axis_to_type()); + + case axis_following: + return step_do(c, stack, axis_to_type()); + + case axis_following_sibling: + return step_do(c, stack, axis_to_type()); + + case axis_namespace: + // namespaced axis is not supported + return xpath_node_set_raw(); + + case axis_parent: + return step_do(c, stack, axis_to_type()); + + case axis_preceding: + return step_do(c, stack, axis_to_type()); + + case axis_preceding_sibling: + return step_do(c, stack, axis_to_type()); + + case axis_self: + return step_do(c, stack, axis_to_type()); + } + } + + 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 to type conversion + } + + default: + assert(!"Wrong expression for return type node set"); + return xpath_node_set_raw(); + } + } + + bool is_posinv() + { + switch (_type) + { + case ast_func_position: + 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: + case ast_filter_posinv: + return true; + + default: + if (_left && !_left->is_posinv()) return false; + + for (xpath_ast_node* n = _right; n; n = n->_next) + if (!n->is_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; + +#ifdef PUGIXML_NO_EXCEPTIONS + jmp_buf _error_handler; +#endif + + void throw_error(const char* message) + { + _result->error = message; + _result->offset = _lexer.current_pos() - _query; + +#ifdef PUGIXML_NO_EXCEPTIONS + longjmp(_error_handler, 1); +#else + throw xpath_exception(*_result); +#endif + } + + void throw_error_oom() + { +#ifdef PUGIXML_NO_EXCEPTIONS + throw_error("Out of memory"); +#else + throw std::bad_alloc(); +#endif + } + + void* alloc_node() + { + void* result = _alloc->allocate_nothrow(sizeof(xpath_ast_node)); + + if (!result) throw_error_oom(); + + return result; + } + + const char_t* alloc_string(const xpath_lexer_string& value) + { + if (value.begin) + { + size_t length = static_cast(value.end - value.begin); + + char_t* c = static_cast(_alloc->allocate_nothrow((length + 1) * sizeof(char_t))); + if (!c) throw_error_oom(); + + memcpy(c, value.begin, length * sizeof(char_t)); + c[length] = 0; + + return c; + } + else return 0; + } + + xpath_ast_node* parse_function_helper(ast_type_t type0, ast_type_t type1, size_t argc, xpath_ast_node* args[2]) + { + assert(argc <= 1); + + if (argc == 1 && args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set"); + + return new (alloc_node()) xpath_ast_node(argc == 0 ? type0 : type1, xpath_type_string, args[0]); + } + + 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 new (alloc_node()) xpath_ast_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) throw_error("Function has to be applied to node set"); + return new (alloc_node()) xpath_ast_node(ast_func_count, xpath_type_number, args[0]); + } + else if (name == PUGIXML_TEXT("contains") && argc == 2) + return new (alloc_node()) xpath_ast_node(ast_func_contains, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("concat") && argc >= 2) + return new (alloc_node()) xpath_ast_node(ast_func_concat, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("ceiling") && argc == 1) + return new (alloc_node()) xpath_ast_node(ast_func_ceiling, xpath_type_number, args[0]); + + break; + + case 'f': + if (name == PUGIXML_TEXT("false") && argc == 0) + return new (alloc_node()) xpath_ast_node(ast_func_false, xpath_type_boolean); + else if (name == PUGIXML_TEXT("floor") && argc == 1) + return new (alloc_node()) xpath_ast_node(ast_func_floor, xpath_type_number, args[0]); + + break; + + case 'i': + if (name == PUGIXML_TEXT("id") && argc == 1) + return new (alloc_node()) xpath_ast_node(ast_func_id, xpath_type_node_set, args[0]); + + break; + + case 'l': + if (name == PUGIXML_TEXT("last") && argc == 0) + return new (alloc_node()) xpath_ast_node(ast_func_last, xpath_type_number); + else if (name == PUGIXML_TEXT("lang") && argc == 1) + return new (alloc_node()) xpath_ast_node(ast_func_lang, xpath_type_boolean, args[0]); + else if (name == PUGIXML_TEXT("local-name") && argc <= 1) + return parse_function_helper(ast_func_local_name_0, ast_func_local_name_1, argc, args); + + break; + + case 'n': + if (name == PUGIXML_TEXT("name") && argc <= 1) + return parse_function_helper(ast_func_name_0, ast_func_name_1, argc, args); + else if (name == PUGIXML_TEXT("namespace-uri") && argc <= 1) + return parse_function_helper(ast_func_namespace_uri_0, ast_func_namespace_uri_1, argc, args); + else if (name == PUGIXML_TEXT("normalize-space") && argc <= 1) + return new (alloc_node()) xpath_ast_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 new (alloc_node()) xpath_ast_node(ast_func_not, xpath_type_boolean, args[0]); + else if (name == PUGIXML_TEXT("number") && argc <= 1) + return new (alloc_node()) xpath_ast_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 new (alloc_node()) xpath_ast_node(ast_func_position, xpath_type_number); + + break; + + case 'r': + if (name == PUGIXML_TEXT("round") && argc == 1) + return new (alloc_node()) xpath_ast_node(ast_func_round, xpath_type_number, args[0]); + + break; + + case 's': + if (name == PUGIXML_TEXT("string") && argc <= 1) + return new (alloc_node()) xpath_ast_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 new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_string_length_0 : ast_func_string_length_1, xpath_type_string, args[0]); + else if (name == PUGIXML_TEXT("starts-with") && argc == 2) + return new (alloc_node()) xpath_ast_node(ast_func_starts_with, xpath_type_boolean, args[0], args[1]); + else if (name == PUGIXML_TEXT("substring-before") && argc == 2) + return new (alloc_node()) xpath_ast_node(ast_func_substring_before, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("substring-after") && argc == 2) + return new (alloc_node()) xpath_ast_node(ast_func_substring_after, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("substring") && (argc == 2 || argc == 3)) + return new (alloc_node()) xpath_ast_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) throw_error("Function has to be applied to node set"); + return new (alloc_node()) xpath_ast_node(ast_func_sum, xpath_type_number, args[0]); + } + + break; + + case 't': + if (name == PUGIXML_TEXT("translate") && argc == 3) + return new (alloc_node()) xpath_ast_node(ast_func_translate, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("true") && argc == 0) + return new (alloc_node()) xpath_ast_node(ast_func_true, xpath_type_boolean); + + break; + } + + throw_error("Unrecognized function or wrong parameter count"); + + return 0; + } + + 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; + } + + 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; + } + + 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) + throw_error("Unknown variable: variable set is not provided"); + + xpath_variable* var = get_variable(_variables, name.begin, name.end); + + if (!var) + throw_error("Unknown variable: variable set does not contain the given name"); + + _lexer.next(); + + return new (alloc_node()) xpath_ast_node(ast_variable, var->type(), var); + } + + case lex_open_brace: + { + _lexer.next(); + + xpath_ast_node* n = parse_expression(); + + if (_lexer.current() != lex_close_brace) + throw_error("Unmatched braces"); + + _lexer.next(); + + return n; + } + + case lex_quoted_string: + { + const char_t* value = alloc_string(_lexer.contents()); + + xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_string_constant, xpath_type_string, value); + _lexer.next(); + + return n; + } + + case lex_number: + { + double value = 0; + + if (!convert_string_to_number(_lexer.contents().begin, _lexer.contents().end, &value)) + throw_error_oom(); + + xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_number_constant, xpath_type_number, value); + _lexer.next(); + + return n; + } + + 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) + throw_error("Unrecognized function call"); + _lexer.next(); + + if (_lexer.current() != lex_close_brace) + args[argc++] = parse_expression(); + + while (_lexer.current() != lex_close_brace) + { + if (_lexer.current() != lex_comma) + throw_error("No comma between function arguments"); + _lexer.next(); + + xpath_ast_node* n = parse_expression(); + + 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: + throw_error("Unrecognizable primary expression"); + + return 0; + } + } + + // FilterExpr ::= PrimaryExpr | FilterExpr Predicate + // Predicate ::= '[' PredicateExpr ']' + // PredicateExpr ::= Expr + xpath_ast_node* parse_filter_expression() + { + xpath_ast_node* n = parse_primary_expression(); + + while (_lexer.current() == lex_open_square_brace) + { + _lexer.next(); + + xpath_ast_node* expr = parse_expression(); + + if (n->rettype() != xpath_type_node_set) throw_error("Predicate has to be applied to node set"); + + bool posinv = expr->rettype() != xpath_type_number && expr->is_posinv(); + + n = new (alloc_node()) xpath_ast_node(posinv ? ast_filter_posinv : ast_filter, xpath_type_node_set, n, expr); + + if (_lexer.current() != lex_close_square_brace) + throw_error("Unmatched square brace"); + + _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) + throw_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(); + + return new (alloc_node()) xpath_ast_node(ast_step, set, axis_self, nodetest_type_node, 0); + } + else if (_lexer.current() == lex_double_dot) + { + _lexer.next(); + + return new (alloc_node()) xpath_ast_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) throw_error("Two axis specifiers in one step"); + + axis = parse_axis_name(nt_name, axis_specified); + + if (!axis_specified) throw_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 throw_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) throw_error("Unrecognized node type"); + + nt_name = xpath_lexer_string(); + } + else if (nt_name == PUGIXML_TEXT("processing-instruction")) + { + if (_lexer.current() != lex_quoted_string) + throw_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) + throw_error("Unmatched brace near processing-instruction()"); + _lexer.next(); + } + else + throw_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 throw_error("Unrecognized node test"); + + xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step, set, axis, nt_type, alloc_string(nt_name)); + + xpath_ast_node* last = 0; + + while (_lexer.current() == lex_open_square_brace) + { + _lexer.next(); + + xpath_ast_node* expr = parse_expression(); + + xpath_ast_node* pred = new (alloc_node()) xpath_ast_node(ast_predicate, xpath_type_node_set, expr); + + if (_lexer.current() != lex_close_square_brace) + throw_error("Unmatched square brace"); + _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); + + while (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) + { + lexeme_t l = _lexer.current(); + _lexer.next(); + + if (l == lex_double_slash) + n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); + + n = parse_step(n); + } + + 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 = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set); + + // 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 = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set); + n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 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 + xpath_ast_node* parse_path_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 (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 (_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) throw_error("Step has to be applied to node set"); + + n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); + } + + // select from location path + return parse_relative_location_path(n); + } + + return n; + } + else return parse_location_path(); + } + + // UnionExpr ::= PathExpr | UnionExpr '|' PathExpr + xpath_ast_node* parse_union_expression() + { + xpath_ast_node* n = parse_path_expression(); + + while (_lexer.current() == lex_union) + { + _lexer.next(); + + xpath_ast_node* expr = parse_union_expression(); + + if (n->rettype() != xpath_type_node_set || expr->rettype() != xpath_type_node_set) + throw_error("Union operator has to be applied to node sets"); + + n = new (alloc_node()) xpath_ast_node(ast_op_union, xpath_type_node_set, n, expr); + } + + return n; + } + + // UnaryExpr ::= UnionExpr | '-' UnaryExpr + xpath_ast_node* parse_unary_expression() + { + if (_lexer.current() == lex_minus) + { + _lexer.next(); + + xpath_ast_node* expr = parse_unary_expression(); + + return new (alloc_node()) xpath_ast_node(ast_op_negate, xpath_type_number, expr); + } + else return parse_union_expression(); + } + + // MultiplicativeExpr ::= UnaryExpr + // | MultiplicativeExpr '*' UnaryExpr + // | MultiplicativeExpr 'div' UnaryExpr + // | MultiplicativeExpr 'mod' UnaryExpr + xpath_ast_node* parse_multiplicative_expression() + { + xpath_ast_node* n = parse_unary_expression(); + + while (_lexer.current() == lex_multiply || (_lexer.current() == lex_string && + (_lexer.contents() == PUGIXML_TEXT("mod") || _lexer.contents() == PUGIXML_TEXT("div")))) + { + ast_type_t op = _lexer.current() == lex_multiply ? ast_op_multiply : + _lexer.contents().begin[0] == 'd' ? ast_op_divide : ast_op_mod; + _lexer.next(); + + xpath_ast_node* expr = parse_unary_expression(); + + n = new (alloc_node()) xpath_ast_node(op, xpath_type_number, n, expr); + } + + return n; + } + + // AdditiveExpr ::= MultiplicativeExpr + // | AdditiveExpr '+' MultiplicativeExpr + // | AdditiveExpr '-' MultiplicativeExpr + xpath_ast_node* parse_additive_expression() + { + xpath_ast_node* n = parse_multiplicative_expression(); + + while (_lexer.current() == lex_plus || _lexer.current() == lex_minus) + { + lexeme_t l = _lexer.current(); + + _lexer.next(); + + xpath_ast_node* expr = parse_multiplicative_expression(); + + n = new (alloc_node()) xpath_ast_node(l == lex_plus ? ast_op_add : ast_op_subtract, xpath_type_number, n, expr); + } + + return n; + } + + // RelationalExpr ::= AdditiveExpr + // | RelationalExpr '<' AdditiveExpr + // | RelationalExpr '>' AdditiveExpr + // | RelationalExpr '<=' AdditiveExpr + // | RelationalExpr '>=' AdditiveExpr + xpath_ast_node* parse_relational_expression() + { + xpath_ast_node* n = parse_additive_expression(); + + while (_lexer.current() == lex_less || _lexer.current() == lex_less_or_equal || + _lexer.current() == lex_greater || _lexer.current() == lex_greater_or_equal) + { + lexeme_t l = _lexer.current(); + _lexer.next(); + + xpath_ast_node* expr = parse_additive_expression(); + + n = new (alloc_node()) xpath_ast_node(l == lex_less ? ast_op_less : l == lex_greater ? ast_op_greater : + l == lex_less_or_equal ? ast_op_less_or_equal : ast_op_greater_or_equal, xpath_type_boolean, n, expr); + } + + return n; + } + + // EqualityExpr ::= RelationalExpr + // | EqualityExpr '=' RelationalExpr + // | EqualityExpr '!=' RelationalExpr + xpath_ast_node* parse_equality_expression() + { + xpath_ast_node* n = parse_relational_expression(); + + while (_lexer.current() == lex_equal || _lexer.current() == lex_not_equal) + { + lexeme_t l = _lexer.current(); + + _lexer.next(); + + xpath_ast_node* expr = parse_relational_expression(); + + n = new (alloc_node()) xpath_ast_node(l == lex_equal ? ast_op_equal : ast_op_not_equal, xpath_type_boolean, n, expr); + } + + return n; + } + + // AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr + xpath_ast_node* parse_and_expression() + { + xpath_ast_node* n = parse_equality_expression(); + + while (_lexer.current() == lex_string && _lexer.contents() == PUGIXML_TEXT("and")) + { + _lexer.next(); + + xpath_ast_node* expr = parse_equality_expression(); + + n = new (alloc_node()) xpath_ast_node(ast_op_and, xpath_type_boolean, n, expr); + } + + return n; + } + + // OrExpr ::= AndExpr | OrExpr 'or' AndExpr + xpath_ast_node* parse_or_expression() + { + xpath_ast_node* n = parse_and_expression(); + + while (_lexer.current() == lex_string && _lexer.contents() == PUGIXML_TEXT("or")) + { + _lexer.next(); + + xpath_ast_node* expr = parse_and_expression(); + + n = new (alloc_node()) xpath_ast_node(ast_op_or, xpath_type_boolean, n, expr); + } + + return n; + } + + // Expr ::= OrExpr + xpath_ast_node* parse_expression() + { + return parse_or_expression(); + } + + 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* result = parse_expression(); + + if (_lexer.current() != lex_eof) + { + // there are still unparsed tokens left, error + throw_error("Incorrect query"); + } + + return result; + } + + 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); + +#ifdef PUGIXML_NO_EXCEPTIONS + int error = setjmp(parser._error_handler); + + return (error == 0) ? parser.parse() : 0; +#else + return parser.parse(); +#endif + } + }; + + struct xpath_query_impl + { + static xpath_query_impl* create() + { + void* memory = global_allocate(sizeof(xpath_query_impl)); + + return new (memory) xpath_query_impl(); + } + + static void destroy(void* ptr) + { + if (!ptr) return; + + // free all allocated pages + static_cast(ptr)->alloc.release(); + + // free allocator memory (with the first page) + global_deallocate(ptr); + } + + xpath_query_impl(): root(0), alloc(&block) + { + block.next = 0; + } + + xpath_ast_node* root; + xpath_allocator alloc; + xpath_memory_block block; + }; + + xpath_string evaluate_string_impl(xpath_query_impl* impl, const xpath_node& n, xpath_stack_data& sd) + { + if (!impl) return xpath_string(); + +#ifdef PUGIXML_NO_EXCEPTIONS + if (setjmp(sd.error_handler)) return xpath_string(); +#endif + + xpath_context c(n, 1, 1); + + return impl->root->eval_string(c, sd.stack); + } +} + +namespace pugi +{ +#ifndef PUGIXML_NO_EXCEPTIONS + xpath_exception::xpath_exception(const xpath_parse_result& result): _result(result) + { + assert(result.error); + } + + const char* xpath_exception::what() const throw() + { + return _result.error; + } + + const xpath_parse_result& xpath_exception::result() const + { + return _result; + } +#endif + + xpath_node::xpath_node() + { + } + + xpath_node::xpath_node(const xml_node& node): _node(node) + { + } + + xpath_node::xpath_node(const xml_attribute& attribute, const xml_node& parent): _node(attribute ? parent : xml_node()), _attribute(attribute) + { + } + + xml_node xpath_node::node() const + { + return _attribute ? xml_node() : _node; + } + + xml_attribute xpath_node::attribute() const + { + return _attribute; + } + + xml_node xpath_node::parent() const + { + return _attribute ? _node : _node.parent(); + } + + xpath_node::operator xpath_node::unspecified_bool_type() const + { + return (_node || _attribute) ? &xpath_node::_node : 0; + } + + bool xpath_node::operator!() const + { + return !(_node || _attribute); + } + + bool xpath_node::operator==(const xpath_node& n) const + { + return _node == n._node && _attribute == n._attribute; + } + + bool xpath_node::operator!=(const xpath_node& n) const + { + return _node != n._node || _attribute != n._attribute; + } + +#ifdef __BORLANDC__ + bool operator&&(const xpath_node& lhs, bool rhs) + { + return (bool)lhs && rhs; + } + + bool operator||(const xpath_node& lhs, bool rhs) + { + return (bool)lhs || rhs; + } +#endif + + void xpath_node_set::_assign(const_iterator begin, const_iterator end) + { + assert(begin <= end); + + size_t size = static_cast(end - begin); + + if (size <= 1) + { + // deallocate old buffer + if (_begin != &_storage) global_deallocate(_begin); + + // use internal buffer + if (begin != end) _storage = *begin; + + _begin = &_storage; + _end = &_storage + size; + } + else + { + // make heap copy + xpath_node* storage = static_cast(global_allocate(size * sizeof(xpath_node))); + + if (!storage) + { +#ifdef PUGIXML_NO_EXCEPTIONS + return; +#else + throw std::bad_alloc(); +#endif + } + + memcpy(storage, begin, size * sizeof(xpath_node)); + + // deallocate old buffer + if (_begin != &_storage) global_deallocate(_begin); + + // finalize + _begin = storage; + _end = storage + size; + } + } + + xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(&_storage), _end(&_storage) + { + } + + xpath_node_set::xpath_node_set(const_iterator begin, const_iterator end, type_t type): _type(type), _begin(&_storage), _end(&_storage) + { + _assign(begin, end); + } + + xpath_node_set::~xpath_node_set() + { + if (_begin != &_storage) global_deallocate(_begin); + } + + xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(ns._type), _begin(&_storage), _end(&_storage) + { + _assign(ns._begin, ns._end); + } + + xpath_node_set& xpath_node_set::operator=(const xpath_node_set& ns) + { + if (this == &ns) return *this; + + _type = ns._type; + _assign(ns._begin, ns._end); + + return *this; + } + + xpath_node_set::type_t xpath_node_set::type() const + { + return _type; + } + + size_t xpath_node_set::size() const + { + return _end - _begin; + } + + bool xpath_node_set::empty() const + { + return _begin == _end; + } + + const xpath_node& xpath_node_set::operator[](size_t index) const + { + assert(index < size()); + return _begin[index]; + } + + xpath_node_set::const_iterator xpath_node_set::begin() const + { + return _begin; + } + + xpath_node_set::const_iterator xpath_node_set::end() const + { + return _end; + } + + void xpath_node_set::sort(bool reverse) + { + _type = xpath_sort(_begin, _end, _type, reverse); + } + + xpath_node xpath_node_set::first() const + { + return xpath_first(_begin, _end, _type); + } + + xpath_parse_result::xpath_parse_result(): error("Internal error"), offset(0) + { + } + + xpath_parse_result::operator bool() const + { + return error == 0; + } + const char* xpath_parse_result::description() const + { + return error ? error : "No error"; + } + + xpath_variable::xpath_variable() + { + } + + 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(!"Invalid variable type"); + return 0; + } + } + + xpath_value_type xpath_variable::type() const + { + return _type; + } + + bool xpath_variable::get_boolean() const + { + return (_type == xpath_type_boolean) ? static_cast(this)->value : false; + } + + double xpath_variable::get_number() const + { + return (_type == xpath_type_number) ? static_cast(this)->value : gen_nan(); + } + + 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(""); + } + + const xpath_node_set& xpath_variable::get_node_set() const + { + return (_type == xpath_type_node_set) ? static_cast(this)->value : dummy_node_set; + } + + bool xpath_variable::set(bool value) + { + if (_type != xpath_type_boolean) return false; + + static_cast(this)->value = value; + return true; + } + + bool xpath_variable::set(double value) + { + if (_type != xpath_type_number) return false; + + static_cast(this)->value = value; + return true; + } + + bool xpath_variable::set(const char_t* value) + { + if (_type != xpath_type_string) return false; + + xpath_variable_string* var = static_cast(this); + + // duplicate string + size_t size = (strlength(value) + 1) * sizeof(char_t); + + char_t* copy = static_cast(global_allocate(size)); + if (!copy) return false; + + memcpy(copy, value, size); + + // replace old string + if (var->value) global_deallocate(var->value); + var->value = copy; + + return true; + } + + bool xpath_variable::set(const xpath_node_set& value) + { + if (_type != xpath_type_node_set) return false; + + static_cast(this)->value = value; + return true; + } + + xpath_variable_set::xpath_variable_set() + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) _data[i] = 0; + } + + xpath_variable_set::~xpath_variable_set() + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + { + xpath_variable* var = _data[i]; + + while (var) + { + xpath_variable* next = var->_next; + + delete_xpath_variable(var->_type, var); + + var = next; + } + } + } + + xpath_variable* xpath_variable_set::find(const char_t* name) const + { + const size_t hash_size = sizeof(_data) / sizeof(_data[0]); + size_t hash = hash_string(name) % hash_size; + + // look for existing variable + for (xpath_variable* var = _data[hash]; var; var = var->_next) + if (strequal(var->name(), name)) + return var; + + return 0; + } + + 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 = hash_string(name) % hash_size; + + // look for existing variable + for (xpath_variable* var = _data[hash]; var; var = var->_next) + if (strequal(var->name(), name)) + return var->type() == type ? var : 0; + + // add new variable + xpath_variable* result = new_xpath_variable(type, name); + + if (result) + { + result->_type = type; + result->_next = _data[hash]; + + _data[hash] = result; + } + + return result; + } + + 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; + } + + 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; + } + + 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; + } + + 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; + } + + xpath_variable* xpath_variable_set::get(const char_t* name) + { + return find(name); + } + + const xpath_variable* xpath_variable_set::get(const char_t* name) const + { + return find(name); + } + + xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables): _impl(0) + { + xpath_query_impl* impl = xpath_query_impl::create(); + + if (!impl) + { +#ifdef PUGIXML_NO_EXCEPTIONS + _result.error = "Out of memory"; +#else + throw std::bad_alloc(); +#endif + } + else + { + buffer_holder impl_holder(impl, xpath_query_impl::destroy); + + impl->root = xpath_parser::parse(query, variables, &impl->alloc, &_result); + + if (impl->root) + { + _impl = static_cast(impl_holder.release()); + _result.error = 0; + } + } + } + + xpath_query::~xpath_query() + { + xpath_query_impl::destroy(_impl); + } + + xpath_value_type xpath_query::return_type() const + { + if (!_impl) return xpath_type_none; + + return static_cast(_impl)->root->rettype(); + } + + bool xpath_query::evaluate_boolean(const xpath_node& n) const + { + if (!_impl) return false; + + xpath_context c(n, 1, 1); + xpath_stack_data sd; + +#ifdef PUGIXML_NO_EXCEPTIONS + if (setjmp(sd.error_handler)) return false; +#endif + + return static_cast(_impl)->root->eval_boolean(c, sd.stack); + } + + double xpath_query::evaluate_number(const xpath_node& n) const + { + if (!_impl) return gen_nan(); + + xpath_context c(n, 1, 1); + xpath_stack_data sd; + +#ifdef PUGIXML_NO_EXCEPTIONS + if (setjmp(sd.error_handler)) return gen_nan(); +#endif + + return static_cast(_impl)->root->eval_number(c, sd.stack); + } + +#ifndef PUGIXML_NO_STL + string_t xpath_query::evaluate_string(const xpath_node& n) const + { + xpath_stack_data sd; + + return evaluate_string_impl(static_cast(_impl), n, sd).c_str(); + } +#endif + + size_t xpath_query::evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const + { + xpath_stack_data sd; + + xpath_string r = evaluate_string_impl(static_cast(_impl), n, sd); + + 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; + } + + xpath_node_set xpath_query::evaluate_node_set(const xpath_node& n) const + { + if (!_impl) return xpath_node_set(); + + xpath_ast_node* root = static_cast(_impl)->root; + + if (root->rettype() != xpath_type_node_set) + { +#ifdef PUGIXML_NO_EXCEPTIONS + return xpath_node_set(); +#else + xpath_parse_result result; + result.error = "Expression does not evaluate to node set"; + + throw xpath_exception(result); +#endif + } + + xpath_context c(n, 1, 1); + xpath_stack_data sd; + +#ifdef PUGIXML_NO_EXCEPTIONS + if (setjmp(sd.error_handler)) return xpath_node_set(); +#endif + + xpath_node_set_raw r = root->eval_node_set(c, sd.stack); + + return xpath_node_set(r.begin(), r.end(), r.type()); + } + + const xpath_parse_result& xpath_query::result() const + { + return _result; + } + + xpath_query::operator xpath_query::unspecified_bool_type() const + { + return _impl ? &xpath_query::_impl : 0; + } + + bool xpath_query::operator!() const + { + return !_impl; + } + + xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables) const + { + xpath_query q(query, variables); + return select_single_node(q); + } + + xpath_node xml_node::select_single_node(const xpath_query& query) const + { + xpath_node_set s = query.evaluate_node_set(*this); + return s.empty() ? xpath_node() : s.first(); + } + + xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables) const + { + xpath_query q(query, variables); + return select_nodes(q); + } + + xpath_node_set xml_node::select_nodes(const xpath_query& query) const + { + return query.evaluate_node_set(*this); + } +} + +#endif + +/** +* Copyright (c) 2006-2010 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. +*/ \ No newline at end of file diff --git a/LifeGraphics/pugixml.hpp b/LifeGraphics/pugixml.hpp new file mode 100644 index 0000000..caa2180 --- /dev/null +++ b/LifeGraphics/pugixml.hpp @@ -0,0 +1,1131 @@ +/** +* pugixml parser - version 1.0 +* -------------------------------------------------------- +* Copyright (C) 2006-2010, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) +* Report bugs and download new versions at http://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_PUGIXML_HPP +#define HEADER_PUGIXML_HPP + +#include "pugiconfig.hpp" + +#ifndef PUGIXML_NO_STL +namespace std +{ + struct bidirectional_iterator_tag; + +#ifdef __SUNPRO_CC + // Sun C++ compiler has a bug which forces template argument names in forward declarations to be the same as in actual definitions + template class allocator; + template struct char_traits; + template class basic_istream; + template class basic_ostream; + template class basic_string; +#else + // Borland C++ compiler has a bug which forces template argument names in forward declarations to be the same as in actual definitions + template class allocator; + template struct char_traits; + template class basic_istream; + template class basic_ostream; + template class basic_string; +#endif + + // Digital Mars compiler has a bug which requires a forward declaration for explicit instantiation (otherwise type selection is messed up later, producing link errors) + // Also note that we have to declare char_traits as a class here, since it's defined that way +#ifdef __DMC__ + template <> class char_traits; +#endif +} +#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 + +// Include exception header for XPath +#if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS) +# include +#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 + +#include + +// 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. 'text' + 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; + + // 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 < / >& stream); + xml_writer_stream(std::basic_ostream >& stream); + + virtual void write(const void* data, size_t size); + + 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 xml_attribute_struct* xml_attribute::*unspecified_bool_type; + + 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 as a number, or 0 if conversion did not succeed or attribute is empty + int as_int() const; + unsigned int as_uint() const; + double as_double() const; + float as_float() const; + + // Get attribute value as bool (returns true if first character is in '1tTyY' set), or false if attribute is empty + bool as_bool() 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(double rhs); + bool set_value(bool rhs); + + // 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=(double rhs); + xml_attribute& operator=(bool rhs); + + // 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; + + protected: + xml_node_struct* _root; + + typedef xml_node_struct* xml_node::*unspecified_bool_type; + + 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/value, or "" if node is empty or it has no name/value + const char_t* name() const; + 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 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 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); + + // 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); + + // 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_single_node(const char_t* query, xpath_variable_set* variables = 0) const; + xpath_node select_single_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; +#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; + + // 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 + + // Child node iterator (a bidirectional iterator over a collection of xml_node) + class PUGIXML_CLASS xml_node_iterator + { + friend class xml_node; + + private: + 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*(); + xml_node* operator->(); + + 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: + 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*(); + xml_attribute* operator->(); + + const xml_attribute_iterator& operator++(); + xml_attribute_iterator operator++(int); + + const xml_attribute_iterator& operator--(); + xml_attribute_iterator operator--(int); + }; + + // 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) + }; + + // 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&); + const xml_document& operator=(const xml_document&); + + void create(); + void destroy(); + + xml_parse_result load_buffer_impl(void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own); + + public: + // Default constructor, makes empty document + xml_document(); + + // Destructor, invalidates all node/attribute handles to this document + ~xml_document(); + + // 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 + + // Load document from zero-terminated string. No encoding conversions are applied. + xml_parse_result load(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(); + + // 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]; + + // Non-copyable semantics + xpath_variable_set(const xpath_variable_set&); + xpath_variable_set& operator=(const xpath_variable_set&); + + xpath_variable* find(const char_t* name) const; + + public: + // Default constructor/destructor + xpath_variable_set(); + ~xpath_variable_set(); + + // 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* xpath_query::*unspecified_bool_type; + + // 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); + + // Destructor + ~xpath_query(); + + // 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; + + // 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 + // 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(); + + // Get parse result + const xpath_parse_result& result() const; + }; +#endif + + // XPath node class (either xml_node or xml_attribute) + class PUGIXML_CLASS xpath_node + { + private: + xml_node _node; + xml_attribute _attribute; + + typedef xml_node xpath_node::*unspecified_bool_type; + + 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; + + // 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); + + // 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; + + xpath_node* _begin; + xpath_node* _end; + + void _assign(const_iterator begin, const_iterator end); + }; +#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&); +} +#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&); +} +#endif + +#endif + +/** +* Copyright (c) 2006-2010 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. +*/ \ No newline at end of file diff --git a/LifeGraphics/pugixml/contrib/foreach.hpp b/LifeGraphics/pugixml/contrib/foreach.hpp new file mode 100644 index 0000000..efe6d6d --- /dev/null +++ b/LifeGraphics/pugixml/contrib/foreach.hpp @@ -0,0 +1,102 @@ +/* + * Boost.Foreach support for pugixml classes. + * This file is provided to the public domain. + * Written by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + */ + +#ifndef HEADER_PUGIXML_FOREACH_HPP +#define HEADER_PUGIXML_FOREACH_HPP + +#include "pugixml.hpp" + +/* + * These types add support for BOOST_FOREACH macro to xml_node and xml_document classes (child iteration only). + * Example usage: + * BOOST_FOREACH(xml_node n, doc) {} + */ + +namespace boost +{ + template struct range_mutable_iterator; + template struct range_const_iterator; + + template<> struct range_mutable_iterator + { + typedef pugi::xml_node::iterator type; + }; + + template<> struct range_const_iterator + { + typedef pugi::xml_node::iterator type; + }; + + template<> struct range_mutable_iterator + { + typedef pugi::xml_document::iterator type; + }; + + template<> struct range_const_iterator + { + typedef pugi::xml_document::iterator type; + }; +} + +/* + * These types add support for BOOST_FOREACH macro to xml_node and xml_document classes (child/attribute iteration). + * Example usage: + * BOOST_FOREACH(xml_node n, children(doc)) {} + * BOOST_FOREACH(xml_node n, attributes(doc)) {} + */ + +namespace pugi +{ + struct xml_node_children_adapter + { + typedef pugi::xml_node::iterator iterator; + typedef pugi::xml_node::iterator const_iterator; + + xml_node node; + + const_iterator begin() const + { + return node.begin(); + } + + const_iterator end() const + { + return node.end(); + } + }; + + xml_node_children_adapter children(const pugi::xml_node& node) + { + xml_node_children_adapter result = {node}; + return result; + } + + struct xml_node_attribute_adapter + { + typedef pugi::xml_node::attribute_iterator iterator; + typedef pugi::xml_node::attribute_iterator const_iterator; + + xml_node node; + + const_iterator begin() const + { + return node.attributes_begin(); + } + + const_iterator end() const + { + return node.attributes_end(); + } + }; + + xml_node_attribute_adapter attributes(const pugi::xml_node& node) + { + xml_node_attribute_adapter result = {node}; + return result; + } +} + +#endif diff --git a/LifeGraphics/pugixml/docs/images/caution.png b/LifeGraphics/pugixml/docs/images/caution.png new file mode 100644 index 0000000000000000000000000000000000000000..5adc3779c79aed3e28deaa8b460c8926d9750122 GIT binary patch literal 426 zcmV;b0agBqP)k*hxe|RCr!>&R257 zFbo7x0-$<#?IpeE{>L-4!bn5M!5{I6*>&AN6Tpn`17#K818UdlBmae2ww5}M_!cPp z7M-}_5U6cL{9NkPZVafw1GU-JQ)f;5Ax*kJoEn3t9i&QXk;{PO9s{~)W&ReDf( zb<1CcM*jnF)64U6p>_8U)Qw;ba<93%pTHVeZUAUtxgnqdaznt)vA{k6$cCoJ6=aMZ U)~j4F4FCWD07*qoM6N<$f}i-u*8l(j literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/docs/images/dom_tree.png b/LifeGraphics/pugixml/docs/images/dom_tree.png new file mode 100644 index 0000000000000000000000000000000000000000..2e28ce8d04871a69a876bd24e35c950d193bf4d8 GIT binary patch literal 8182 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 literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/docs/images/dom_tree_thumb.png b/LifeGraphics/pugixml/docs/images/dom_tree_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..8b0ba85b02500c816346b0f5860e980bac329ad1 GIT binary patch literal 3127 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 Ro}vH%002ovPDHLkV1k6f+93b{ literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/docs/images/home.png b/LifeGraphics/pugixml/docs/images/home.png new file mode 100644 index 0000000000000000000000000000000000000000..124a56b79c06ac54823317108673f70d8b6459a3 GIT binary patch literal 217 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7T3?v)swEqJsl>na**Z=?jcXxI;JJ=mPayTI_ zMon2^=FFLwFJIcWbxWYXZ(3TKxQO7>ci#+v>KRLd{DK)Ap4~_Ta*{n=978JNYELW_ za&QoFdsr`WHLYKv>$lJ?HrH?#-%lK$><)Cpl7<>o?hlHdw91=@;~n4 zX{)&r5D^d&7!hC@lx?v5^wP6Omooq1+rLQX!*-SZ)z|jwdfq+xjg5D*li|YP;GaP2 O7(8A5T-G@yGywqbhEnYS literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/docs/images/next.png b/LifeGraphics/pugixml/docs/images/next.png new file mode 100644 index 0000000000000000000000000000000000000000..fbb2fdc5b71e94fb4f97d8d2f759b1853d61893e GIT binary patch literal 204 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7T3?v)swEqJsl>na**Z=?jcXxIe8yj!ix+T!x zS4~-A#qy<(9zDE#`BFk$jI)ECxQJjTL!}o`J!46bUoeBivm0qZPMD{QV@O3@YX5HD z0}2ApFC8Kq1uq#nZCkJ~ft8CbVU|gVNt~Yi-IEuehF-gT^3%@txH%8bFD+yaF?T3E zapm#y*M*z+TRCa;e9jim_x)%vNqWbbH&4aIRZf^yO{__E0ouUe>FVdQ&MBb@08z+H A^Z)<= literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/docs/images/note.png b/LifeGraphics/pugixml/docs/images/note.png new file mode 100644 index 0000000000000000000000000000000000000000..e960b3956fa2606b5f88ea663ad4870878e45d3c GIT binary patch literal 357 zcmV-r0h<1aP)rM0!S&d$!>-rncu=kM?Dl$4acy}gW#jMmoH#>U2bdwX+pb8BmBQc_Z5V`GGb zgj!l!x*1NL0002wNkl!9L5QI^>X9pzP$^Gvd$uZu(`T44=py8RHVfBP! zl1%Cc#Oxd%5k52Z1)emlG-W|h@K&(?A+qpU)*Y1iDrhIr@SPHE4Q9!4=4SxCffi%1 zi`vls!=p?WZBEY=1zdCLUS>8@Ee{y!03vU{?Yd=&)C|vN52#JemwkZY>)v+>ZSUAD?Fk$3<5E?>z+UJ_4R0{0x&-gt128Y==e+-CqVcG zU2z9g#v5o?JOGk&kxNcI0fU`pZpto>HvmlYh`N@4RR;(vbHieK00000NkvXXu0mjf Db8Vn) literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/docs/images/prev.png b/LifeGraphics/pugixml/docs/images/prev.png new file mode 100644 index 0000000000000000000000000000000000000000..ceadadaa82787bb46352f1cf7116a4d6b29b5270 GIT binary patch literal 198 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7T3?v)swEqJsl>na**Z=?jr=_JC8yj!ix@E=k zrGfswYRU?iFJJ2J>~MClONfgR7ZJR7&uaxxJ!46bUoeBivm0qZPJpM2V@O3@YTrRl z21Op0&pX22<#X5V{eJbqbs?RDSu8i7^(6TyeC9YUFm0);(nH6|KN4d)>r^+LdZhZ{ upHD*N3YOlix9iPr#4Ueow14`eeZu>HoBZu{HaH42o59o7&t;ucLK6VzPE7>> literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/docs/images/up.png b/LifeGraphics/pugixml/docs/images/up.png new file mode 100644 index 0000000000000000000000000000000000000000..23e4aec4f1245fb59c2376c2e769c4de27687e78 GIT binary patch literal 224 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7T3?v)swEqJstpJ}8*Z=?jr=_JiJJ@a8x@E=k zrQMw!moHxm^!HU$R+u?+=FuaE6XIeXJ$fiEBKS#0eIHO4V@Z%-FoVOh8)-mJil>WX zNJZS#GYfee3`ANUP8Wz_Z8O~Ed_1w)ukrYV5JPrF3mz5G8Mj+^x3oXMGT(nrru(LH z#jx|d9(@jfC%o|}6S3}U{eIf`0<&;I(0Z{5x2z7?%{;qq_)2yadL4V8Qxqr3ZyDnL UQq0<|3TPjLr>mdKI;Vst0J1An*#H0l literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/docs/images/vs2005_link1.png b/LifeGraphics/pugixml/docs/images/vs2005_link1.png new file mode 100644 index 0000000000000000000000000000000000000000..6da6366afa49d26758d5217497fa273078c1188d GIT binary patch literal 25698 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�`y;QMMGV4t>kPt=%?cFA$N%n>t$0*w;hv5i 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 kKQZFpA;9YjA3m5tS06NHXHvj+t literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/docs/images/vs2005_link1_thumb.png b/LifeGraphics/pugixml/docs/images/vs2005_link1_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..86882e0105d3fafc4642fe650bdd1a44a6e06b58 GIT binary patch literal 2531 zcmV<92^{u`P)1pw6vlBuP9R8^o+9PFVmtMlq35tF2x?>StwTcF8C= zJdzI8`do05EfS-Iw|(X|xiH0_q_D_{QOA@Gaup>Nmb?8+DtxSi`*K5wZ7V^JofjF< zdRPy`rlvqdfKjQ?*s?w{#6M{5wSG;7r+a4_^t)jCPXOL0Bv%~3Q|5EXDbyvfU4+FoIs(=LmQ}e70;CXJC`n-f z4vRwoopPZAIW9~b^?(~KQXGO4;M6%7uz@RR8(1F;QkbS zQmRSX4x{z}KoT!U@%aR&xFKYMoMMukT;KSH=lx(HNyjT1A3{ zuugg55oNcddL;*L*p&qkCxIEabNlzFY7pw#U7}>u(AJvyYRW85I{GAb=mlv=7hjM? zO-`YsCzPd#zoQ~A5LZvvu(Q??Tu}q91PqB3`iD`{QL&P==}e=^H;#%(>KkXd=?LXQ z7+-9tp)|Ol%|O$7RfZW6WOKkcO`=#4cZG}!n8^fVSBjz+oWo|TpLh0ae7g|>m2lmg8_vI`h?-Ik%cfl zxmc}MmnN%|)gqJm;nLzgN!mw*jq+R&X`l!t!g`p#G&OZ;>e9reD-#pdsfmdzm#ZI7 zemr^i$M>X5IY1Nx;6@ETSO9npmnXP5gYySFt!<_4|-TUxR*o z+5bwp_Th&!{OZ-2($y>H7u*Ly*uE8n&ypg`r}oJU5n7!RWtoLM5zs&pEAv5!`xYuX zR}I|SZTKJ%E-uuKbS+xgG!Si?3_+Q z$z0$o!ab|>5;(`}9Fd{u1w@u!{RTmC#92g1DlTtA3K^~zr)RT56*Xilf~vBo5JlIb ze?slSb}vC#dh_cFlp_FET3Dq$1~nk~2V5&ocT%KB-VJ-?f(V0P{}B8llE~Pig@xd}ua?uv&>1yi1Uw4;M_jDpK;ojd)YI z?MhaOmH@g1--nCFIDsY?(9p`=Ouf`!>yZj5gen9_lpLj77^V*>tUMO}G})N*nIEYj zP$;t<1R)*&Yciksj9SP@=}7s!G>A75^JL8rZIPm`sLz+GA;jm zDfXNpN7kVvPC|B{dhr^L1uJQ1P1*TB6JTYwOL*n>5^CdXg>5wLwmiR}b*=cVVPBXn zFQ69y;x1wHaDJ}o+K4x?Evy)zhF{9oksxU2jMtBN=9=wwTwibBL~5_Eugmk+@O>Y&*;(r^yz-a!rvGH0o9oI3;M_HI z`e5&1060^k&$Qa(dA4?#PRF$xjBq`m{I5Pd%mu4ak5I_y_}Z#zYYj)graQBgq1pP} zj!U@g1t*XDpo*C{qY}sq6DK+ihzwSN$qtYaW~i)n$>BG~+84_6`<&O)jix_BRJN}m z^E=Sx69R)fCE#$a*%9_7+7@S)A08$q1It7h*tA|}{zw7ZtiM2cr%zoqwyEn%ju!Dt z6pbVy`{lnd^@37}hAgJBRrW05?E3?kbTaA5H_i}BCj_T^HL z6hlNNq#a7=zQ;kO%v!a26hplZGp%|p{#K4a=+t1e)>4RVlzLTYg+dq?YH-Dbb(W$T zyawIB4=v=7Wp8Up8E~vUfR+g_2An15RNq-@aey@22X4HuQ3KAJUf=@>+E}fqUpU!b z>tG;(=t$6GnAYe>K${DQ*rjZMl3Auw_L{A$onNTeZDt1-hzh|8ZMr%galX%A0#JMX z<`*}yEi*gtw{*TWUD(b)+&Ki^Rl}&SOD%RwD@Q6AwS8J} zKTPXsK~Zze0&D}Ej;~eEs0*<{8+Ku}Tq}@BJ;Xl@goS!^Wpq2mt002ovPDHLkV1nKJyrBR9 literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/docs/images/vs2005_link2.png b/LifeGraphics/pugixml/docs/images/vs2005_link2.png new file mode 100644 index 0000000000000000000000000000000000000000..9f75a4f773a17a8eb052a33aa677fbfc0fc4c942 GIT binary patch literal 18063 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 Y-WPrGna9oxn;3w=)78&qol`;+0RC#k<^TWy literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/docs/images/vs2005_link2_thumb.png b/LifeGraphics/pugixml/docs/images/vs2005_link2_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..64954d5560682a1e6b34a419bef63918aaaf4881 GIT binary patch literal 1901 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 K{uRzT^#1@hU*hBd literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/docs/images/vs2005_pch1_thumb.png b/LifeGraphics/pugixml/docs/images/vs2005_pch1_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..96df958c91a58b0d98579bacc1d3340441391a90 GIT binary patch literal 4511 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 B1iAnK literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/docs/images/vs2005_pch3_thumb.png b/LifeGraphics/pugixml/docs/images/vs2005_pch3_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..60fa8f80d6e2103cd42e942a6e3c12f31aa87714 GIT binary patch literal 1944 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 JvV=qp{~z3Tqv!wt literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/docs/images/vs2010_link1_thumb.png b/LifeGraphics/pugixml/docs/images/vs2010_link1_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..223b429b729a5e34f9c56143d3daf8c6cabcc8d7 GIT binary patch literal 1765 zcmVup;=03YKMxBqOqHHq^!BS#MA1_cGBD9;pXY{=*#`+ul)b}xofd+000JX zNklpw`g+MSEZnf6wv)lMnG z{DEX0&Y7AVC{fNI>?0D zVTm}GPD%)ltS$uYQtDi$l{|G$e`F!6UrQaa$Pk>4ae&n+fh>8DzDictV2KfEX&`4E z$px_cF`7II>FK*pTB{il6MX!q5stpbYK9Ru0oRxdYM32+l*bS}nknZ?Dydh1P@Wnz zgMaiP$Ky|LfFs7qI840{9u>gQ0)Zh^5CRzcdtXm#z8t)L^$qV%A}W7GzZ+(yr`hq* zWcKk{QEcNz?x#6Qk07c5y#r9+yVz&);fV@9R|m{{AvV0O{QJA*JT`mEdR9+!Ipcm zVH5mn_)bgx@+b_0aU92h<~7_)K=4AAAkI$ob|D10Ad0=GVc4b1_8QRFw96zRY?hws zNk|ZAl0hI8g5ZyP`W2PW$233h!(~?Jk~ej*gh5rA%4qYHk0pF*y3v0t9h#8dfPZSJ zrf5QF+gzUU?%sGiLlbUix4XyCguZ-}hb4Uf*x7jwPne1%J=jHXB}@cM#hS;=nP3Mf z;2cKy0+mJaf#CQrS)t_(Yu-P-%109X;o&5I%ZQaqmU5Y@JX1<6vI0{OpL+-Z z*byKgMLq{0k-!w{_{?tIv_=twaR3)Y&uYJc{Ra9`#GX7^98y;3%_>MQV)%y{@KzF% zyWc<(675ZQ$W!)crM?{X)T@>)jAKXpHgvnoOlT|pv!^}eNAf)T?2~U%B%(3VCXIX< zqKDMpczUxOT4=c3|dDWY^jlM5-l zIxL7t(I*wK0M3&NnrUrQ3QaD!P=?e(0hgy0#LEn61!~QW%U+th(y9XmGM3B)Acjr? z%Q7}7GE5+bk2?uQLVBrWx>us+fnqwrPm@kU`d)G|#1Nt(QlrLz;5);ecSYpcBTCV{Yo3#o=e)LgxCnJP zn|&#W7>pp3MqmINFv$iIS)N2N5;6yn3kmJh?MpGxq+Cu7FLL38R?Z5gk_epT8sHF| z13n}Wlrvr{?Vb19r4Z`V5X@7`s38c>E#0_SJ}{q9RD6Q>FB9QV@3mud53Mq&A= zFa@;PMOazc`y{%eb z4jV3{nHtN<{(;(prqp*3L@H1zh^+2WxV0|~MIdmzH*#joIMuUoU zfClwWt-BPw7zf8gJ{XN}kQ@p5w=`rI3)#I&1P<>RhU}tJMR1n98|MiLowrg!u<*d` zHd@#S0M8cwk^H_l)!0MaMFmA(Qvj>#E+qmZ02)q4hK>kdL(L#VgOW3!00000NkvXX Hu0mjf!kHd>#>(;;W(8y10&z^nC`ISZgtH5d|{|1*0G_n7*v!9cAs?$|eD#B1ULAMHMw$ z@mN*46csh)Yuc}6H7xFFig0P?21;4M%<{DFYpd!xp!MCg^$e&Ul^Ym72-OAjjIC-F z1^X3vtt=%Y^tH?_%uFmD3@tqh3}J!hw<>hd<$B^)whn&A$?guU^3T$2?4Iy?d!xLn zpE*CZat*hA9uV#5Fz|#x+OK-xk%F7Yi~6Uu65)|?&tIfF1qp`_+XV*t_`R<3A@sz) z(F^}H=wcKQi1iDJY4eAr_!T{VQ`Yb85E5;R2`g{*@yrRkU*qo}k$h1U7+m6?7!mny z(AOd|IwCkGy+2sYF!gKPySFYG8Bzr&Zxd5(^I{W{-nk}UOhikjrl%yQXNKkE%tiSo zyl=HAuS?D<%E)?eRr2@a`|!k^ir~VIdo>GUO&1es4=OSmnlj$!JXD?-xFezdlq|j~BeJs4CkkHY=}b`BYn- z)%e4u_bjceZmlw>vALn4rERR~)kU>s@WA)rFaPS>`)b<1?2TFeeUXL?H(!l8QIo5)Z5=P-<|ul@7vqa(}1b-!4I`|t1G`S!u_ z-oftF#l^+>$KB)8qm#q4i;44#QzNUxf0u$`c~?~l7D~vXhR=|=s6BiEgHd)|{)cxv z7h2Ml!?cQA#x^hU%LY#7z_ndSCZHF8sA8#vY=f|KOnYKK8MN|AKVy{>s#cZ z?`#nrMGqV5S>e%+XT(KN6m+u}{1DZ7&K=JXy6f?e;>VA7E&|`3 zI>IAf);UhqIM$g(umA#lOa_+scFx}~C$~0!ys-7O_4J)tANV5s?N(g!)QqiftL>8a z(wvH_s;WwG&STkG_xiMfY5!GfTHZIj(jx&5QKTjyT`^wdfvPxm7q-;PZj^K}&ScCBFPC#udHOo`NCgR44B;awky z>_uH&gcx?~r=pvcJfx}v}^?l`ubV%oYO?OiJ%Qu>5=hSk%s{S zPkl4Ku@XaPU_G3WPhPAeK*kCtgDZQ+XDHD6&}LTZ9*mkA#U|u9a#K8AxuxylZ~r=( zyT{LMvm$en9x0zALU1n?aAiJ9>_o)Yq*a!)M@HcJ>rU6{iFy|yT&V==p)r*OIu=|$K5I9Tr5BAyTS15)J z8G4i@iX7!)t8kvd8fnK8f<6vJbx1U7!Fck@mTtcd-CZtJMv~H%%rfjDEP*#|=lW66 zi!Hqz+dr>@e)a|`;6ggs0ZKI58jY1VkVgb7EMA3*kg$oE9q^kix%{I?k8T3+U$3YC zuy1=E-}!8vJk>Gzb91f0l3WK&dZNg=K-S|S@a?rug1$}9_%*}U?s=@Xxq>E?hO@8^ zRZ-YC`y)Y3WQkG>fPuKz&^qA`xAQYdWFO=ul6EpZ6Hr_8JQyC9{C3wJ-dcg8y`mBEr{2to%53cC zCYMqL&fdj^2k^6e0-MBzU*WTcXvwVs7?aj*;Y=22Ov-C!>q*a6>t1tHU-~|4v7*UDlK0Y=C5|K%cAmxPKa$qQN zpP|?W4A=%|k->h3DdT3y%rWeP-K7sDFCs)M&^%sS{%11E7(3~1*X3p<#L8yKPV=|x zQgDY0i~VcV_bZPHuVczw(x%z1pml151$iM74MSkP!2OM3F=lF1EYjt6y3Y^7*ap7S z`>FZ&-=Hypu%Ow*<>uXj2JevOTq&D$FR(aVB7Y`3*N14PQ9)*Yf)BpHcXE1N z1W`}A7Pr5sD9_1?kzEu`IZgCcK2WIM3uG_MGTCV{Rv9A zVzIc~WzwPClQAqd+Pp5F!@Ob@g$SE1#kHH&?^! z@~{n7WGo_9Fk%hBQo}u7Fg%T)>|8vOUT${fuHBneauQ2B$>3mcnd%nsic9OO{c@6_ z!=se&x0LGUd=1fN+JW3Do#kW+fl1fj5IsMahi66FaCV(-ZTFI+s8(%F7jtyLgGI&Qc7IxK+M|&^ww?W;P|adGGF*V8WT1V+S@xt zsyW|!p~;>y$czJ2sB}e)x2hxSZl8}Nk;3n=ArOTu`5qMEGM>|KcA_&ra6pJg?&X8! zI#rHuQ9epYI#fbkkn!=UUJ+)8(d3u2z#~@F&c>L7x>&gc*U~ z&2wL5-q#zgz(3S#$#7O0J=H3jV&X$g=4_nnAB&y>zMh_|cI!H{dfD+zn@`wLjP2SWz z8slExrVI(#PlOW@DNDo>zbWfjC!y{ODJ+x<&pP7(9-?9?!BVp{90r1Ww&8l!Z&4Xi zgAn0awP(_MCloVBFeU(nl89D;>D=asB?Xj4a|1CGGOwTaMNju-V$hFTTi)nMrjcXo zWG(TVC6)lNM{hM@{J&tB;0E;3l*%DH1uvGf*BPmO+VyB_vvf3iwe@LPOBlZ75UHa; z=t~|1n$X?ne^`~v7Gqomo~yd=t&iQZF%4Ac43*LI++WxD`7z1SvFNccKkIK_@}F}o z%|B&|$fyLuGj+J)Mm;4pr63M~XPCW0NI{BR6#f;Q!@k|h_Walf8(JZ5DM!HgtkCzy?o=;oPZ7i z$tzWo9;dPn{sUGvM&MSA9tdn*tOv=%D4%}b0y@No=Mp~0#1_0D=@tV3RU0o8EY{*N zf`KWF$(%!x#YSpU_Yk@5E-=?+2nSO0Pe?=2>Per~LW(GBLEpgF>311`NAe&7-oX)D zAozA(90tgx{oO?4DKFS{uE4;CC88{G)50|&c8mx$usavbvYTDVvEPFE=@50H&h|Cs zRJAt0ChS&iX|SK~QloSGZlbm8i35kH>A+{T*@`Nlc{73T`mXW25o+}C4pyfkHkW;5#Nym& zwkru!OK zLDz@YNe4S{#YDwI^aBTfRtLW@7=5<#AnDY=?3i3#v-B3p%53}Vp3SPSRB;7s#S>7k zTpfZimXU&_RQD+=kk}yQY8?Lu8XB}vs@IDX0~?yp46{GH*53~k9S%$+Q*)qL@*bFm zLm`5%?_77U>(cjUmB-?36!WjRE+E)8yl4jr;Ag2+R`M6RbmJ85XU!N~RqNseXnN#~cvd~ZKJfAe^j7)(jkPEv>?xIiGkNZ=nbFpn6 z4isIa6x66pS&hBN3EOiCqgS#5=w2#^5{b>om8qbiK!{60#%*hExcdh-wpxo9WY1Y2 zl1L?Sws-x}XJeUd^i;nm`l0HM&cN#nw;Qide#0ZXuH_vI-92=U3_M@K?co4Ga{u^P}vohk>VAxfO1vQ*OU9|@5_~0sKbe)Pn zgaTBQEKWKKcB0YNZfdOm%wC!gCV=EMlLj!`zDS6uCX3Sq4F)A(1oB+|{~U zIuu9AjAB^XoSVByW6-&x@69c$I>gRnV#{Bs3nS23%0$4G zU_p59JW8-u1fHV62Jq0bBf?5~9?q4A4-sR(Q(w6(j<=T3x-6pKC}YZ!Fkc z`?l^#YoMR6S!}t=&E3{IgIXs%8)WExKJRY*HT{9`SjfPpR?GQ-a5b)o)M*lC+;pQE z-Q>g7XO*F)HI4{dh{VNzdujne58d8BHPm%ZGxjsfm-bwH+0|3byN&ONJu0?7-GOcE zmdo{Hb{D&L^uhvR^7~#--_$a{VoQQ47_T9CuG{9C=o6oO|D191B8gs??&R!9Z~+O2 zl#4C?rMMD=biMe@Ya#xZDo%~}AyX*Si=-|U9rADImAm@wnUV55y)ie$% z!&kXjOo2lIyRSbxqSlTh_GcF%AJ=oh+1H_)r}wC^Y_5T+1rCD-;Kf&8W~Z ze*X#umGmS)RDsZ2liJ6&?{F-2R7xmM{0vmyCd=X6PjBzqiNR@YDgF3MRS64X(s<`Z zx>y`&y?CT+tn77{(D`ueOYO4Ta7DHgLI0_y)u~+GNWm&$)#M@DTblcSy*Z{*iYu%a zEq1xfF;}#*>3bOAsmjj*nrZrGc=t1+4nnz>qwZOscM-ZvQ@e42xQsUU)^eja@0br{ zbK{0b0NqHgSPRFG0}$q2T1t)rrmIsFE6~Y-DrYyo!SCfT&~MmumUofjZEEiomv7j4 zF|2pxA_ajh758$9NopndTq;z`f9R+BGZz$9H`g&Uuk_c zB@fJPw?xt2c3Vv(T9|L+Q+UW>k=o*7TuPt0Ga(*Ui&U912AW$4a$D*p`(04DJ_LFhwvvn~7v4h%2+VFx# zz~fBea_Gmq$s?hi6F5&gqC%74VZ5R8st-IV%KNeYRcw;x6+m}dth(!NO$|m_S=T+u zWJ@$Ebvp?K@GeoGnSmU> z87dE1ENQo}$yTvrcqPoks z&_N*vh_H6M%{Qi!W405$t|utw>n1N;$k+JEz1F)H2PmHIb@jf&YGMj(EZHDh`jVB`|om^tG>=dfMr{| zlxM_ZD?eMsi=}^sqn*ufo~_-FH}L-}1=nih8+?i(|Za^PBdW6ft`b$64Qd7ojPlQ(ZKRvkTqDIYkTK%tRFXWH!A7r0)yv3B5wO1T zDlnvj7Av%h)GFUD9)W|jsU|-eY_1||SZY6?6X*l`nqDJg!#|yx-#31@v@fnMbdyLr z%M(g{9k+N~(xY$0_bxQRnu4n9`(H+uXf(^{$K^Pe{8r!b>ijG<>(KQkw@;y@l+Sp)PvWxb zg5>x4sh%7)+x$4ro82Jap60R574Mz;Vj|LbbR@yyn19d2(w;~z(*h?6k<&epkNvjn zA-&GxnDE@ue}5o4x=f()URGJzz`(%%esr`)F=f@DyzjZANzhUZ<9E=5CW(CXWGv5Bc1qZG0|Ci57) zo$lcV*;f>vqBwEo_h6xl=qPzxTZI#Cr!()G(`uuhK!Tsx{3hR=C=BY{I2adq@m`_y zN1R{B#8Yz%Os9g@5|LXeIbIE*+UaZI4^XpvFVmW9_Ls)(;X>|~HT$D4BsfD0c!Ucg zTrCUa7}Yri8+SQ8LW}mV#ioBv`hno|fF-{_Pu(fgOqaJb98BJ3&nLy6IHC_qkcS6u zdkb-X6ws)`@RG@h(L|PprUIMU6xs<2BG z7E+U3hpADG`b^$oA_2r;^=TKwLepZJj=##(^KPp3Lvj?mv(KO>WlGl%4=>c+UvhgODjYKfDu8o0?=1}b8O^km@#A>7 z+M0>rNhg{YMST+BCP?k>z&@MkuUbet`KV(@?6>XaC+&0e+ic=xG-n<}U-4|Ba(+f?R{Eq@qO9)sRXKMAIIGl>=R4*g*nE>E2Ifr${V1q|Ek3S{ zr6qOGZ^Mi{!vuQVKxJxJX6fj~VK6Nc;UDYF(vGuZqYT+93iEB=tRp`m>gae#1`j?7 z<4vF+Lk+N~fI}B~D4qiSc&#WqJieVD2HJ)CjAd&RIUL(&C_v-dP z_(Vy}QJ?PEW`fV+h5^(=J`&$DMOPy+U%#l+UM(Q&s?%Zd+W;oX)*q&K3wTdj=ZL7G zH>?Ri2{WZkL2!J$os=6PJ;Jcss0^g2%=NC!BwYKbrAo)))2>#tJL_f^f{H`h!NCUdknwnD|T}(UbXt2jGkeiSRJsY)rnOEO=vKco>HF^ zASwtCVbrtANuwg0b}VfcV*jp8zzLvOyGZa_;8P9n2RuvdcBMzv*Td1D$e**MipCu^ zxa%74jN8sCIR;S)$(zh+VZ-!KECp%qJsHh!`B)6bvv<8 zNJ^a`%!ZtZ6*iv_r5y~>;c}vJ;+s)VYNyY1lxb2jb#cl3xW+b-U?<`vS zT+76B(v_I+Uw{$#G8}`mtLsgDT`KGv&pE63_#bDmcZ$B(jyRYs4*c1unONpe^2}+r z{nf;`nFYnh2CGQzRSTd8jxewy#tJl~by)o#ji`HfGZy4oIf(c~2*-Md+zIbP8$-Dm z^auA8hSHowWjOW|JK(*R_H!gZgbW4!kdmso9;?{E^D@LmUSIE`M<8nYfpqO7P}#uj&iAXn!frx$eVHE^9DZZ3I{bB4Rd3qgo~&mTy@T0@+<*Z z0E>BIpW%~ObG#&j*X=`QSS9N1O`q+5j!}z*)!j^b#0p?s!C3u;{f)BT{>z?>8k)OX zoqdofpY)>ntnjF%HBmprtbgrJ*|~3MFzUMsOw8CTyF-OI*LoO(~sd6@8&Pud3jTQ^+B{FB%DpGu|G&g)7~H5WoZFZI7AbC zMWUvGn2VK<*XZk8%=E!{clbtSmy(rFiraDaN9U@~t+BF?1$nxV!=8|c7+@*=t<2r= zm;3nB{gIPj?z>XQX+v>#GH1Js24>4{af#Z8vv*z9^_zqo*$cbqUN5(|*BpIzx$*^;$qCSNXVL)TMU6S>;#cy%Ua(MBa$o|QgQDTqYi`o^!}cFWveyi<2h5|S0miiR zxJRwDFbi{w^dYi}h-^Z66hAcd9BdHLVS;FLlGBI)4?WzG&I_UeIY5-KKeps)cLX1-OOfa8(64;HS?=% zlFc`y2Dl>T(yu5VIAln-ROzFC>F%72TMAi>%(! zY(T^b*yoyoe_U9tE~H?n>P)+b1-rnX5%G_G=r%+TiT|l2poF>#7B-Wnhu9Z*^}2N? z^9)TFbk8B$uXicMjC#JWAz~LDd-xG$zcI3JYURfd*_~w4Jz#pqR9TY=3sS0LEq{M z$QqD`GoZmLMI4t5YY-@BR_%AUahH?-MhZP+#YdVue=yDkG5T5e<@T=m?|D|a8xs;| zid+P`YTvzkw*eta=KP6v%APa~BMvGTBT>LAI|2gdv~L`J6rAOF#;$hNzSSk?t?&Mc zzYb>)X;pihg^qE$52Ek5=Zs*VF5gKa-HX;tR_v3opG}XZ|3T&1~rY zsnzcxR`Hu3ZI#)8WE8^g{UftGe!gPOWF*6VeMjr_llAY`ueXkACh503{KWs;iMVle zakr8EOUbff*US^KuoQw&G!YcIq^k2I|A_6<*~do<|KB9&KY#HT0{<0FL|Qb+Ic80< z0gG64&4luQIQ(~@@xOuxq(~kg@OQW9iO)9nJDz{$7lfKv{=;>}^~=mDLhZOv1>Y#o zH*p1FsIW_vCRi}%oBWxf2t&$q)UC@ea-b!i93psfH%KxmrDDUm#?;7cU7N;$KZ z9?$N}1|b4Y9w>xR07+K~bvl2p6q3S4PMj&cyU&h~tNd5?f0fuM;KHEz8-NyqJ(d#C`kzI?eSsvA*yl!wQx+y3S=4rEs($y$&hNqaKksqI;TCeP_!(< zr5=HOi=JI;Y&ej+T(nLl9A3*foNs)c!CtOT5>!_q6$vHLV;?JJrXd+rz+J`JJ5uD8 zLp7A?7yiMW%@X$TWG(MD3n*7QQshRrSU$5gBT|SBu=r6TaKckrL}G9;jgS6{a{~61 zIbT$MK8(!;*>!9nFI|o;f$!3K4Xe^wfnPlHPuKhAd+*X2v?h?;Bh3#K`a*e+LCv&X zNFxl>9RTG9V7NKJ>E!2M>#&N}43;Ut-Fc$vJA9OiU;3s-)OMUWNYJ0XoOXCa`KPat z($ixPa?c8GxFyQUa{O(zTejsD=j$uh2>oMzE7+?sz4z4e4RXs1ffYq=Je5plv?5deVYP+ab}4=LWq&FWFh z)2Y=*%=*@aZ(3cPZ0D21RkARWGzioaD||}|BLjrX15-6ervCz=m0%1vKgjibfmuuY zjAqui(Mbg-&Vp!LQS_^ZwbGmUee^m-g}Bt%03sKG7*p-$i800BwYEO0UERjW<27w z>~O^*ZlF*I69sd6T;1+AePjfh0jWj*fj0J z1YHW8#q1PyEC$_#O8EAjD8iUg1q_dYDM{sFaj`WX12T8w<0#FN=ZT)vvg-(eT6Xr| z4uu=FWHMXdXnn=Q51nyB_K zI&EcNyUd0W%f8f+Ev0%yUv*2$sPeU@$fxJ70aUMdrHVi~p1+|w!DnPCy)^+410B}6 z@{v>KO$6%xAdbcv4qH$vRho^l^xz^s`F$%487mGsEhKw^fqwEL9}5v2K+rx=K0M5D z>JSsv)lGEWA4U>r_w|JYNr3NNg}1gVk1Nyt{<&varu4QAZELHI%82uFpq#g4sohAh zv9U)DyD|1QOIIwFG6&6PYpYcb7=FyGtm>4p{XO4m(?!E5=iS3$Mc!|P(#+Gje?InJ zCRUTRwsot}Iak(Y@l`OEAGjiZ$WRnI=;Ni*XMF*T(2CqD!}m(LknHp1jQ3W{kbZay z_hpS6d{rrgzqPL<8-yaU>^}(gU;gXRRYpamUOi@g`gfWd$+PPN|EVpGl62NX#U=)P zl?J>VZ{7j3o12^OusywD?E7^=VXJ@tw~gfg*Qmwhn$@-)HYE|{?5_+vvz82FFM$0H&_G2Tc!MQ}(dlj6TDgiPkkJOUoa zF&KI0f~EgM!XzWN@a*wP1^(c>OpHy85m*r=oE72!qvHRS z&HtADY3_Q`DP#FMKbNwoYf!1Iw^hjEf{n_mgku~Ej=URx+r!Pi!_7+ID(sDGIF#ibmq zx4i_oux)vxXJ6n6ljP$OQ0A8q*dYu(o89~i>+uyoSS}?PRJV|AW*C5nz%c#+GC16^ zMda8BeC}us_(7pLwWo~0<3ZSfN9Wt64?oJrn8Th}f-R?uC9Qa<3j$tBnWGtryV=AO zemSl2y~w&%JkKWvpwnrma&#(s6b<0}-y-9v!ir}j0ell|wLf}}FQEXc1Yhj*@AwMO zOZ3Zs!}pcuCALd%<_dB}@v>hQ(-XBYzz09(Cg?RW0UPa0?DBWAOwDW1obvtnhe!*0 z<%1MB_Hn%MgV&%9$+N)y1aE>cblS`Dk<2Q66K%90BQe&@MiKRcE+wWb8y-&9l#%FsqJ=v94Sd~rcU zOl;DAo8+k+`Bf20Aot`{`~?aTeKS%GBBx-|QyiurPV6j{mh4#$$L-qvKtL7C(7mZW z!r0pgNHt$f4N{_rK!14GG9J|s(3XZws)-e{Ur4k9FFMPK4 z7KvdUj(v+1S~}l5IC`#Z;govIK@zk{$`P=rynzrZ3_41Ay8F{{kj3qw{)MtdLF69kWj8-*PUi*!Syiu3+M~q?2FEE8nhQxvb2PA~ZnX3DD*nBK(sI$@GfRjhN-{ z^LjxG6>#As?yGD_M|7X*)`!J#ShGYVL`w(X`$YvQjZYCp1!$ltORjk)=Y*o$kfA)< z3T69r`UwtyhQeSfY5Mk&4AdpWqCX;6oQ@w;;nu>{7)LV1fqFCUid6Zb;|wZgVdk>#@r|gv76r? zQrzp5pOrus@z|2+RBxHb`@J`&@SO1eyjJdy1iU$z_Q6i(-$48nx09o$nK>3MiKUF1fyGgT zRdry{{>+yxNCM7=PlH$AkPvKYPB%NWk=#K@K6o>FjD#!U>>$iP8pP7NukH3!BFi^& zEXdKvnj(awTazpcm%F{_1XX*z2UV{gD$dut65u&Lz}G_Cg0ipq-bI@pF=;+3_L^M` zng;u)IOziLY=M5kLD8m+~d&Dm-Cyq4_K48`ugKMommew zRG`C8?$wqA>6^)0L*H+Ma5?4K>j|LV+g$$t8fx3B)g$jei(ygDaKQO&tLm4CJT4WT zTKCg@^F2kX&9&(lvhp}GXDAT0+@mBF#Q6+={$d88*IUM<_gE;jLPi&`zsw0h^H&>u ziFw9zpQt#&>@dXETQA$CxtvSDLj;7`dx0kEEz*d*L^^_Q`#=$rfx`EE#=IZRnvX?~ z;MG2s9Tm$)aJf)6Se<-w9)K<8?p7_hh2GixhN7NG;?A2YIse+#q!gnk8G}I zYcIkBFa5hVsGqm)=aYhgfKOuX5$zTGdAc*!>}>YSokf~iv~};e{q!k&#|HDIGJ(NS z77mVf9wkUm%FFK<7-F^#%tqnoQ0NALe>mY0G85}`S#`U742}Nm8urVT1Ya?U5e!-0 z!H>(r@<^bl5^o*B{&yongbVnJ|8nW$r$9!8U|PZvKI*dE5fOTmBqFc8UUTOjcZ8l2 z?yVfSpFjbaX~py}CQMAVXLoPhAgHu@6Z6ID+HGkv9&SJZfXqU-p0 zO1^|A0Pz1A@gFHQQzRtIbdP|G2XOOi*=s zQTmcH*k}0O{slb&z)y~av7^|96cCVC+MRTMis6)3_)x+F?VakQg%rEHCiHlx3NT`! zLoh@KD(Wz2JGCZwnRxYX z{MHTA@!$8##D>)vl+w`!_v%oAc;Bm{53Oxz^6|;P_RDMc$s--4g%;+se8Wia-K+51 z)N30KwPlqzpBXjP)p#5$JRElHSKcj*53#xSb3}lM)q+h6-N2WbL4%JU;V8hut9G^G z5^#`$;$6g(2LdS6>n-EzEL{kFE?PB^_}A*>H@^~xiQe^ zI=Jj?9=V$?RD2~^uQ}gTDW*NIvwz0aU^?7F19{LX=$(fVIY-EiC}tkSi$HB5}EgN6E^<_eUS(C~N*W?G05Pu- zI+TzO5ePwq{SSPi69fdzbAmhX2Bq7X88UxLQ12Elr{{%}!=^}h$neiU$^9Yw2lb}6 zz25X|}* zJ;#10$f1tIRIBe>qP{t#gV#himYl`eQ9{)G(JE50Fg^B68Cgbb1MlOxg?J}#kLSAY z(sD<7)tS4WA>{spZUo>BG#cQ5tkWmsNB4dO|ICYdcwxn(2yS`ffxDr&th<7nBMKIM zw&I)$Flx`}s|fGW*mv}ys$+1=M<(*=OOAzM~IF)Kj~T{IL5l{%RIEDXban_Yj8 zfZtI3s8j>RdLBb+dBcw=kb@-HxwrZ^f%TW|MYpH$TYdQmf3P@fuhQAdQ01HbQp@MG zpF~d))tGix9i~@M_8t;|%2A~~y*GYEUPUDQ6jXZPP)`0TB7xMYVsO}VOfywROA{~DM)cLvw%%quiG6N-SJjBEHFm+! z(P)_Lbd|ec_4dqhyxk84cH#4kG})6lwuF)g#*XTy(!3vnp_M?)cu|DJ>WETEwqlo# zSH(m3c@PR!#faZxvvrHUdT=wTE|Cpw!%2{@%6A^du>EoC-%6Ta%kK@rGkWV+&3baK ziJlLd6aTB>&thYuD&vq6m4eJp+;F(gBNfla@w)_UQZ!#i8*OFOi@6C6@0A~v z8LQD35Xt@0VCPJc|7zxFzfB1R@>nZSzY+$sNlH365wTf%ixq75CCjDdAx zl|&v)-&x+wboR&Dxp@33P7bjT#%N4Z6Zp>?+*P+nA?~R0H5H>nm4vTq$G+UqQ&8K9 zH&`v~q9#x%q_jfi$^mmd)kIf-?`g=wPeX>U%C3_3L7FCN3~Hfh@f0ZyhMuD2aw34R zMLLr1y)=NvP11d$@7bGVv0up*G~rYQ;-MenFal%9dP?@9B3J*hue%?2I@u`+U<(XL}h>x}*gg%LhHeUGv z5@fx7>kGl)(RcAiQRcfOI_&cmu>`XJGH_U@F!o5EhpW3kzYqcJ(RZ6D-3YjpGjGP9 zFmT>teqUsu&&c<0YOYG>@8bts+2!@B_n(xCsKSCY`0&>q|M##7E#3^u!h_BKJK;6F zgZCQh|LhkmP>4U7qR<)3C?`RpuY_WL%KcI-xlVNRUK3?=@ex<0wqq^<61}KbdvR)5 zzEJk)?9{_GfTHP|_B_MfRRmmL-tEt~M@K@Q&D%RBofHD!m%T;XXSqXR#W3h~Y@

*`(7nj*Z_BX(JMzXpQVnH4o#->|!w z%oC5{5$C5wNl)=!S6(n~Ec~m`9nr@ygwdzjfTu{jmtd*yhIbfsK-?h-^rB%V`T4<< z?qLZSh_g+Wd4)LhO(ZeX^y7p7iyHE$bO79m1ehPUiG9i(P%&5h-}@USB% z;-`Qy$+3&#@wHrl^E2W`KX1!<6AaEou4(<#E+Ujw!HtHPe0{9KerXOb^@G>QrM??w z{--NVpICm=Zh;`Sw&iUgb^#h>%dovBYusk`@`!SyEfeox3M17FZyWCB)%ob0eL{DN&F}9uGk5@xhGY^Co}~Pwjsw|GPDph!qhAP7L%t zGFj2dbnrg(UOQzeQc_w4 Ohp8)TDU~T$`2TOT9Us{M literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/docs/images/vs2010_link2_thumb.png b/LifeGraphics/pugixml/docs/images/vs2010_link2_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..34d9dd951c03e98414165a80aadd234a4feedb04 GIT binary patch literal 1820 zcmV+%2jlpOP)>15)7sjq4ZhGtD0w)*rUD_}cvqo) zR4%;kqZ_y`7Wz>M>8x6fdBcV*8WLMVGD|9vC56B8*@I)-w>HM>sWU{$q9hqcS?m(L zp^_vS*cd&SrFZ9$M3FI37)7y-j&;Y*lkKBH8&?fobt%^!G$cmhuXe-m!n^9(2r;;v zp&Oo*^e{A&YOhnV2cxuBQfm3l8IUYl;W;=1GE7`_JuH2eriQ2@Y4N0;;ZeExe+(un zrDK(wwP7!voK*Z#?pw!j7~7%SuhVLV(>&kL9X~%|7~~TYd#Si+?tKwF5Q=YGw$*AX zQ0j;)Arxbpx08y7@1s5g;QWj;2FyR78YmD-&!|rUVCxzUhj;~mO1edXwdr2f9@tpE z(wfWO2$&XPYOwYULkY8)sc6WjjL&Czl3Dk$sZCwXaJPdj!yI>9n7D|`x{}L&QDzt~ z$Ok66FY#bdVhB`dOz-Q5mI=1^j5bn9&2^yfg^ANe1EW5r;QvN|`QQURAcWNJ*dYHE zR${m{3f)Q@so#fEb~S8DE$)z55L2K469r@|G+p7~zF9QncAl*3?$#5~1?3b~lFN3z z&UWiSTj}D&jn1)=^Zakz#wi-n!1YcL_-&1-B?g#uw_C3_>-DFU&A<6h6E|@pa8vge z!w7yp?lSU%Z~8m_25AubMDQhoso|M3v~KbVfBtKT-NbP?5^y{Ufl1N6K?5bK?FWa9 zaR$K>1G`g9zQyQ2?AgI;+b{rt;q9;o7`yB}N}vxA`~pGXl=Wu(2z`NIaNONWpfI*u zV5m!WUyo(5xo&#OC5@d!DKsHg3Z)Q2BUO?O!Lb}HdxEj~w=lv9{*dil;%$Lr%SnVj zbD9cv6l76gcXpz*kSHm%o%lJ*b9M*vzDfx;6lBAlVY&OhQ0~n*VQtSD=BE&_kOpgUvi7nPEv`7|hXp z+DBW3Pm?uSFRut*ln@S>Z~>aNlMB!Qhkl}4y$M=Vt6>4K#fOr@WgF<7-?e7vb5fW> zpr77#ySC+gDJ7iw{*5mRp&5#r1?ARoF75}+jtXvm;k!|nO=ChUzhKri!!!lw(deMZ zP0geqvq6&;K5&616wFjb18S8C-<<|bAkyJKD^x+{lj_ zr-fHLh3?2pXMglkxv?25|Fx}`t7~=(>;pq5g$6Bzqg+{jUQmy^8I1PjW9QG^7ei8|RMg0&$2&7&Ad<+lb zF_0GVCRkxbS%f%DdMjlql)V(z@XqMPgLU5GWnrn)xqDHknN8EO4Xxuu@PR;k`jNZ| z49p0yqYDvT+X1gHC|i?T>xCDu8Yp;eUhSa_{-W@Gw8C?;;xDi*?z+(G6$JwDE>5C|($sa~$H){`To(o(j*5cocd2q2 z^X9$>V?MJkg!Xn6jAbE8wu^Ta4!X15g=uipp8sr*f=vCuvlVusbXHsVnJ4xLtG|be zqSqQZ*#vbMs`^5O3C4g?#7oR@X(|+`B_@kON~|^8EbuL-idb>Ds} + + +pugixml 1.0 + + + + + + + + + +
+pugixml 1.0 manual | + Overview | + Installation | + Document: + Object model · Loading · Accessing · Modifying · Saving | + XPath | + API Reference | + Table of Contents +
Next
+
+
+ + +
+ +

+ pugixml is a light-weight C++ XML + processing library. It consists of a DOM-like interface with rich traversal/modification + capabilities, an extremely fast XML parser which constructs the DOM tree + from an XML file/buffer, and an XPath 1.0 implementation + for complex data-driven tree queries. Full Unicode support is also available, + with two Unicode interface variants + and conversions between different Unicode encodings (which happen automatically + during parsing/saving). The library is extremely + portable and easy to integrate and use. pugixml is developed and maintained + since 2006 and has many users. All code is distributed under the MIT + license, making it completely free to use in both open-source and + proprietary applications. +

+

+ pugixml enables very fast, convenient and memory-efficient XML document processing. + However, since pugixml has a DOM parser, it can't process XML documents that + do not fit in memory; also the parser is a non-validating one, so if you + need DTD or XML Schema validation, the library is not for you. +

+

+ This is the complete manual for pugixml, which describes all features of + the library in detail. If you want to start writing code as quickly as possible, + you are advised to read the quick start guide + first. +

+
+ + + + + +
[Note]Note

+ No documentation is perfect, neither is this one. If you encounter a description + that is unclear, please file an issue as described in Feedback. + Also if you can spare the time for a full proof-reading, including spelling + and grammar, that would be great! Please send me + an e-mail; as a token of appreciation, your name will be included + into the corresponding section + of this documentation. +

+
+
+ +

+ If you believe you've found a bug in pugixml (bugs include compilation problems + (errors/warnings), crashes, performance degradation and incorrect behavior), + please file an issue via issue + submission form. Be sure to include the relevant information so that + the bug can be reproduced: the version of pugixml, compiler version and target + architecture, the code that uses pugixml and exhibits the bug, etc. +

+

+ Feature requests can be reported the same way as bugs, so if you're missing + some functionality in pugixml or if the API is rough in some places and you + can suggest an improvement, file + an issue. However please note that there are many factors when considering + API changes (compatibility with previous versions, API redundancy, etc.), + so generally features that can be implemented via a small function without + pugixml modification are not accepted. However, all rules have exceptions. +

+

+ If you have a contribution to pugixml, such as build script for some build + system/IDE, or a well-designed set of helper functions, or a binding to some + language other than C++, please file + an issue. You can include the relevant patches as issue attachments. + Your contribution has to be distributed under the terms of a license that's + compatible with pugixml license; i.e. GPL/LGPL licensed code is not accepted. +

+

+ If filing an issue is not possible due to privacy or other concerns, you + can contact pugixml author by e-mail directly: arseny.kapoulkine@gmail.com. +

+
+
+ +

+ pugixml could not be developed without the help from many people; some of + them are listed in this section. If you've played a part in pugixml development + and you can not find yourself on this list, I'm truly sorry; please send me an e-mail so I can fix this. +

+

+ Thanks to Kristen Wegner for pugxml parser, + which was used as a basis for pugixml. +

+

+ Thanks to Neville Franks for contributions + to pugxml parser. +

+

+ Thanks to Artyom Palvelev for suggesting + a lazy gap contraction approach. +

+

+ Thanks to Vyacheslav Egorov for documentation + proofreading. +

+
+
+ +

+ The pugixml library is distributed under the MIT license: +

+
+

+ Copyright (c) 2006-2010 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. +

+
+

+ This means that you can freely use pugixml in your applications, both open-source + and proprietary. If you use pugixml in a product, it is sufficient to add + an acknowledgment like this to the product distribution: +

+

+ This software is based on pugixml library (http://pugixml.org).
+pugixml + is Copyright (C) 2006-2010 Arseny Kapoulkine. +

+
+
+ + + +

Last revised: October 31, 2010 at 17:44:02 GMT

+
+ + + +
+pugixml 1.0 manual | + Overview | + Installation | + Document: + Object model · Loading · Accessing · Modifying · Saving | + XPath | + API Reference | + Table of Contents +
Next
+ + diff --git a/LifeGraphics/pugixml/docs/manual/access.html b/LifeGraphics/pugixml/docs/manual/access.html new file mode 100644 index 0000000..d4b38c2 --- /dev/null +++ b/LifeGraphics/pugixml/docs/manual/access.html @@ -0,0 +1,723 @@ + + + +Accessing document data + + + + + + + + + + + +
+pugixml 1.0 manual | + Overview | + Installation | + Document: + Object model · Loading · Accessing · Modifying · Saving | + XPath | + API Reference | + Table of Contents +
+PrevUpHomeNext +
+
+
+ + +

+ pugixml features an extensive interface for getting various types of data from + the document and for traversing the document. This section provides documentation + for all such functions that do not modify the tree except for XPath-related + functions; see XPath for XPath reference. As discussed in C++ interface, + there are two types of handles to tree data - xml_node + and xml_attribute. The handles have special + null (empty) values which propagate through various functions and thus are + useful for writing more concise code; see this description + for details. The documentation in this section will explicitly state the results + of all function in case of null inputs. +

+
+ +

+ The internal representation of the document is a tree, where each node has + a list of child nodes (the order of children corresponds to their order in + the XML representation), and additionally element nodes have a list of attributes, + which is also ordered. Several functions are provided in order to let you + get from one node in the tree to the other. These functions roughly correspond + to the internal representation, and thus are usually building blocks for + other methods of traversing (i.e. XPath traversals are based on these functions). +

+
xml_node xml_node::parent() const;
+xml_node xml_node::first_child() const;
+xml_node xml_node::last_child() const;
+xml_node xml_node::next_sibling() const;
+xml_node xml_node::previous_sibling() const;
+
+xml_attribute xml_node::first_attribute() const;
+xml_attribute xml_node::last_attribute() const;
+xml_attribute xml_attribute::next_attribute() const;
+xml_attribute xml_attribute::previous_attribute() const;
+
+

+ parent function returns the + node's parent; all non-null nodes except the document have non-null parent. + first_child and last_child return the first and last child + of the node, respectively; note that only document nodes and element nodes + can have non-empty child node list. If node has no children, both functions + return null nodes. next_sibling + and previous_sibling return + the node that's immediately to the right/left of this node in the children + list, respectively - for example, in <a/><b/><c/>, + calling next_sibling for + a handle that points to <b/> + results in a handle pointing to <c/>, + and calling previous_sibling + results in handle pointing to <a/>. + If node does not have next/previous sibling (this happens if it is the last/first + node in the list, respectively), the functions return null nodes. first_attribute, last_attribute, + next_attribute and previous_attribute functions behave similarly + to the corresponding child node functions and allow to iterate through attribute + list in the same way. +

+
+ + + + + +
[Note]Note

+ Because of memory consumption reasons, attributes do not have a link to + their parent nodes. Thus there is no xml_attribute::parent() function. +

+

+ Calling any of the functions above on the null handle results in a null handle + - i.e. node.first_child().next_sibling() + returns the second child of node, + and null handle if node is + null, has no children at all or if it has only one child node. +

+

+ With these functions, you can iterate through all child nodes and display + all attributes like this (samples/traverse_base.cpp): +

+

+ +

+
for (pugi::xml_node tool = tools.first_child(); tool; tool = tool.next_sibling())
+{
+    std::cout << "Tool:";
+
+    for (pugi::xml_attribute attr = tool.first_attribute(); attr; attr = attr.next_attribute())
+    {
+        std::cout << " " << attr.name() << "=" << attr.value();
+    }
+
+    std::cout << std::endl;
+}
+
+

+

+
+
+ +

+ Apart from structural information (parent, child nodes, attributes), nodes + can have name and value, both of which are strings. Depending on node type, + name or value may be absent. node_document + nodes do not have a name or value, node_element + and node_declaration nodes always + have a name but never have a value, node_pcdata, + node_cdata, node_comment + and node_doctype nodes never have a name + but always have a value (it may be empty though), node_pi + nodes always have a name and a value (again, value may be empty). In order + to get node's name or value, you can use the following functions: +

+
const char_t* xml_node::name() const;
+const char_t* xml_node::value() const;
+
+

+ In case node does not have a name or value or if the node handle is null, + both functions return empty strings - they never return null pointers. +

+

+ It is common to store data as text contents of some node - i.e. <node><description>This is a node</description></node>. + In this case, <description> node does not have a value, but instead + has a child of type node_pcdata with value + "This is a node". pugixml + provides two helper functions to parse such data: +

+
const char_t* xml_node::child_value() const;
+const char_t* xml_node::child_value(const char_t* name) const;
+
+

+ child_value() + returns the value of the first child with type node_pcdata + or node_cdata; child_value(name) + is a simple wrapper for child(name).child_value(). + For the above example, calling node.child_value("description") and description.child_value() will both produce string "This is a node". If there is no + child with relevant type, or if the handle is null, child_value + functions return empty string. +

+

+ There is an example of using some of these functions at + the end of the next section. +

+
+
+ +

+ All attributes have name and value, both of which are strings (value may + be empty). There are two corresponding accessors, like for xml_node: +

+
const char_t* xml_attribute::name() const;
+const char_t* xml_attribute::value() const;
+
+

+ In case the attribute handle is null, both functions return empty strings + - they never return null pointers. +

+

+ In many cases attribute values have types that are not strings - i.e. an + attribute may always contain values that should be treated as integers, despite + the fact that they are represented as strings in XML. pugixml provides several + accessors that convert attribute value to some other type: +

+
int xml_attribute::as_int() const;
+unsigned int xml_attribute::as_uint() const;
+double xml_attribute::as_double() const;
+float xml_attribute::as_float() const;
+bool xml_attribute::as_bool() const;
+
+

+ as_int, as_uint, + as_double and as_float convert attribute values to numbers. + If attribute handle is null or attribute value is empty, 0 + is returned. Otherwise, all leading whitespace characters are truncated, + and the remaining string is parsed as a decimal number (as_int + or as_uint) or as a floating + point number in either decimal or scientific form (as_double + or as_float). Any extra characters + are silently discarded, i.e. as_int + will return 1 for string "1abc". +

+

+ In case the input string contains a number that is out of the target numeric + range, the result is undefined. +

+
+ + + + + +
[Caution]Caution

+ Number conversion functions depend on current C locale as set with setlocale, so may return unexpected results + if the locale is different from "C". +

+

+ as_bool converts attribute + value to boolean as follows: if attribute handle is null or attribute value + is empty, false is returned. + Otherwise, true is returned + if the first character is one of '1', 't', + 'T', 'y', 'Y'. + This means that strings like "true" + and "yes" are recognized + as true, while strings like + "false" and "no" are recognized as false. For more complex matching you'll have + to write your own function. +

+
+ + + + + +
[Note]Note

+ There are no portable 64-bit types in C++, so there is no corresponding + conversion function. If your platform has a 64-bit integer, you can easily + write a conversion function yourself. +

+

+ This is an example of using these functions, along with node data retrieval + ones (samples/traverse_base.cpp): +

+

+ +

+
for (pugi::xml_node tool = tools.child("Tool"); tool; tool = tool.next_sibling("Tool"))
+{
+    std::cout << "Tool " << tool.attribute("Filename").value();
+    std::cout << ": AllowRemote " << tool.attribute("AllowRemote").as_bool();
+    std::cout << ", Timeout " << tool.attribute("Timeout").as_int();
+    std::cout << ", Description '" << tool.child_value("Description") << "'\n";
+}
+
+

+

+
+
+ +

+ Since a lot of document traversal consists of finding the node/attribute + with the correct name, there are special functions for that purpose: +

+
xml_node xml_node::child(const char_t* name) const;
+xml_attribute xml_node::attribute(const char_t* name) const;
+xml_node xml_node::next_sibling(const char_t* name) const;
+xml_node xml_node::previous_sibling(const char_t* name) const;
+
+

+ child and attribute + return the first child/attribute with the specified name; next_sibling + and previous_sibling return + the first sibling in the corresponding direction with the specified name. + All string comparisons are case-sensitive. In case the node handle is null + or there is no node/attribute with the specified name, null handle is returned. +

+

+ child and next_sibling + functions can be used together to loop through all child nodes with the desired + name like this: +

+
for (pugi::xml_node tool = tools.child("Tool"); tool; tool = tool.next_sibling("Tool"))
+
+

+ Occasionally the needed node is specified not by the unique name but instead + by the value of some attribute; for example, it is common to have node collections + with each node having a unique id: <group><item id="1"/> <item id="2"/></group>. There are two functions for finding + child nodes based on the attribute values: +

+
xml_node xml_node::find_child_by_attribute(const char_t* name, const char_t* attr_name, const char_t* attr_value) const;
+xml_node xml_node::find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const;
+
+

+ The three-argument function returns the first child node with the specified + name which has an attribute with the specified name/value; the two-argument + function skips the name test for the node, which can be useful for searching + in heterogeneous collections. If the node handle is null or if no node is + found, null handle is returned. All string comparisons are case-sensitive. +

+

+ In all of the above functions, all arguments have to be valid strings; passing + null pointers results in undefined behavior. +

+

+ This is an example of using these functions (samples/traverse_base.cpp): +

+

+ +

+
std::cout << "Tool for *.dae generation: " << tools.find_child_by_attribute("Tool", "OutputFileMasks", "*.dae").attribute("Filename").value() << "\n";
+
+for (pugi::xml_node tool = tools.child("Tool"); tool; tool = tool.next_sibling("Tool"))
+{
+    std::cout << "Tool " << tool.attribute("Filename").value() << "\n";
+}
+
+

+

+
+
+ +

+ Child node lists and attribute lists are simply double-linked lists; while + you can use previous_sibling/next_sibling and other such functions for + iteration, pugixml additionally provides node and attribute iterators, so + that you can treat nodes as containers of other nodes or attributes: +

+
class xml_node_iterator;
+class xml_attribute_iterator;
+
+typedef xml_node_iterator xml_node::iterator;
+iterator xml_node::begin() const;
+iterator xml_node::end() const;
+
+typedef xml_attribute_iterator xml_node::attribute_iterator;
+attribute_iterator xml_node::attributes_begin() const;
+attribute_iterator xml_node::attributes_end() const;
+
+

+ begin and attributes_begin + return iterators that point to the first node/attribute, respectively; end and attributes_end + return past-the-end iterator for node/attribute list, respectively - this + iterator can't be dereferenced, but decrementing it results in an iterator + pointing to the last element in the list (except for empty lists, where decrementing + past-the-end iterator results in undefined behavior). Past-the-end iterator + is commonly used as a termination value for iteration loops (see sample below). + If you want to get an iterator that points to an existing handle, you can + construct the iterator with the handle as a single constructor argument, + like so: xml_node_iterator(node). + For xml_attribute_iterator, + you'll have to provide both an attribute and its parent node. +

+

+ begin and end + return equal iterators if called on null node; such iterators can't be dereferenced. + attributes_begin and attributes_end behave the same way. For + correct iterator usage this means that child node/attribute collections of + null nodes appear to be empty. +

+

+ Both types of iterators have bidirectional iterator semantics (i.e. they + can be incremented and decremented, but efficient random access is not supported) + and support all usual iterator operations - comparison, dereference, etc. + The iterators are invalidated if the node/attribute objects they're pointing + to are removed from the tree; adding nodes/attributes does not invalidate + any iterators. +

+

+ Here is an example of using iterators for document traversal (samples/traverse_iter.cpp): +

+

+ +

+
for (pugi::xml_node_iterator it = tools.begin(); it != tools.end(); ++it)
+{
+    std::cout << "Tool:";
+
+    for (pugi::xml_attribute_iterator ait = it->attributes_begin(); ait != it->attributes_end(); ++ait)
+    {
+        std::cout << " " << ait->name() << "=" << ait->value();
+    }
+
+    std::cout << std::endl;
+}
+
+

+

+
+ + + + + +
[Caution]Caution

+ Node and attribute iterators are somewhere in the middle between const + and non-const iterators. While dereference operation yields a non-constant + reference to the object, so that you can use it for tree modification operations, + modifying this reference by assignment - i.e. passing iterators to a function + like std::sort - will not give expected results, + as assignment modifies local handle that's stored in the iterator. +

+
+
+ +

+ The methods described above allow traversal of immediate children of some + node; if you want to do a deep tree traversal, you'll have to do it via a + recursive function or some equivalent method. However, pugixml provides a + helper for depth-first traversal of a subtree. In order to use it, you have + to implement xml_tree_walker + interface and to call traverse + function: +

+
class xml_tree_walker
+{
+public:
+    virtual bool begin(xml_node& node);
+    virtual bool for_each(xml_node& node) = 0;
+    virtual bool end(xml_node& node);
+
+    int depth() const;
+};
+
+bool xml_node::traverse(xml_tree_walker& walker);
+
+

+ The traversal is launched by calling traverse + function on traversal root and proceeds as follows: +

+
    +
  • + First, begin function + is called with traversal root as its argument. +
  • +
  • + Then, for_each function + is called for all nodes in the traversal subtree in depth first order, + excluding the traversal root. Node is passed as an argument. +
  • +
  • + Finally, end function + is called with traversal root as its argument. +
  • +
+

+ If begin, end + or any of the for_each calls + return false, the traversal + is terminated and false is returned + as the traversal result; otherwise, the traversal results in true. Note that you don't have to override + begin or end + functions; their default implementations return true. +

+

+ You can get the node's depth relative to the traversal root at any point + by calling depth function. + It returns -1 + if called from begin/end, and returns 0-based depth if called + from for_each - depth is + 0 for all children of the traversal root, 1 for all grandchildren and so + on. +

+

+ This is an example of traversing tree hierarchy with xml_tree_walker (samples/traverse_walker.cpp): +

+

+ +

+
struct simple_walker: pugi::xml_tree_walker
+{
+    virtual bool for_each(pugi::xml_node& node)
+    {
+        for (int i = 0; i < depth(); ++i) std::cout << "  "; // indentation
+
+        std::cout << node_types[node.type()] << ": name='" << node.name() << "', value='" << node.value() << "'\n";
+
+        return true; // continue traversal
+    }
+};
+
+

+

+

+ +

+
simple_walker walker;
+doc.traverse(walker);
+
+

+

+
+
+ +

+ While there are existing functions for getting a node/attribute with known + contents, they are often not sufficient for simple queries. As an alternative + for manual iteration through nodes/attributes until the needed one is found, + you can make a predicate and call one of find_ + functions: +

+
template <typename Predicate> xml_attribute xml_node::find_attribute(Predicate pred) const;
+template <typename Predicate> xml_node xml_node::find_child(Predicate pred) const;
+template <typename Predicate> xml_node xml_node::find_node(Predicate pred) const;
+
+

+ The predicate should be either a plain function or a function object which + accepts one argument of type xml_attribute + (for find_attribute) or + xml_node (for find_child and find_node), + and returns bool. The predicate + is never called with null handle as an argument. +

+

+ find_attribute function iterates + through all attributes of the specified node, and returns the first attribute + for which the predicate returned true. + If the predicate returned false + for all attributes or if there were no attributes (including the case where + the node is null), null attribute is returned. +

+

+ find_child function iterates + through all child nodes of the specified node, and returns the first node + for which the predicate returned true. + If the predicate returned false + for all nodes or if there were no child nodes (including the case where the + node is null), null node is returned. +

+

+ find_node function performs + a depth-first traversal through the subtree of the specified node (excluding + the node itself), and returns the first node for which the predicate returned + true. If the predicate returned + false for all nodes or if subtree + was empty, null node is returned. +

+

+ This is an example of using predicate-based functions (samples/traverse_predicate.cpp): +

+

+ +

+
bool small_timeout(pugi::xml_node node)
+{
+    return node.attribute("Timeout").as_int() < 20;
+}
+
+struct allow_remote_predicate
+{
+    bool operator()(pugi::xml_attribute attr) const
+    {
+        return strcmp(attr.name(), "AllowRemote") == 0;
+    }
+
+    bool operator()(pugi::xml_node node) const
+    {
+        return node.attribute("AllowRemote").as_bool();
+    }
+};
+
+

+

+

+ +

+
// Find child via predicate (looks for direct children only)
+std::cout << tools.find_child(allow_remote_predicate()).attribute("Filename").value() << std::endl;
+
+// Find node via predicate (looks for all descendants in depth-first order)
+std::cout << doc.find_node(allow_remote_predicate()).attribute("Filename").value() << std::endl;
+
+// Find attribute via predicate
+std::cout << tools.last_child().find_attribute(allow_remote_predicate()).value() << std::endl;
+
+// We can use simple functions instead of function objects
+std::cout << tools.find_child(small_timeout).attribute("Filename").value() << std::endl;
+
+

+

+
+
+ +

+ If you need to get the document root of some node, you can use the following + function: +

+
xml_node xml_node::root() const;
+
+

+ This function returns the node with type node_document, + which is the root node of the document the node belongs to (unless the node + is null, in which case null node is returned). +

+

+ While pugixml supports complex XPath expressions, sometimes a simple path + handling facility is needed. There are two functions, for getting node path + and for converting path to a node: +

+
string_t xml_node::path(char_t delimiter = '/') const;
+xml_node xml_node::first_element_by_path(const char_t* path, char_t delimiter = '/') const;
+
+

+ Node paths consist of node names, separated with a delimiter (which is / by default); also paths can contain self + (.) and parent (..) pseudo-names, so that this is a valid + path: "../../foo/./bar". + path returns the path to + the node from the document root, first_element_by_path + looks for a node represented by a given path; a path can be an absolute one + (absolute paths start with the delimiter), in which case the rest of the + path is treated as document root relative, and relative to the given node. + For example, in the following document: <a><b><c/></b></a>, + node <c/> has path "a/b/c"; + calling first_element_by_path + for document with path "a/b" + results in node <b/>; calling first_element_by_path + for node <a/> with path "../a/./b/../." + results in node <a/>; calling first_element_by_path + with path "/a" results + in node <a/> for any node. +

+

+ In case path component is ambiguous (if there are two nodes with given name), + the first one is selected; paths are not guaranteed to uniquely identify + nodes in a document. If any component of a path is not found, the result + of first_element_by_path + is null node; also first_element_by_path + returns null node for null nodes, in which case the path does not matter. + path returns an empty string + for null nodes. +

+
+ + + + + +
[Note]Note

+ path function returns the + result as STL string, and thus is not available if PUGIXML_NO_STL + is defined. +

+

+ pugixml does not record row/column information for nodes upon parsing for + efficiency reasons. However, if the node has not changed in a significant + way since parsing (the name/value are not changed, and the node itself is + the original one, i.e. it was not deleted from the tree and re-added later), + it is possible to get the offset from the beginning of XML buffer: +

+
ptrdiff_t xml_node::offset_debug() const;
+
+

+ If the offset is not available (this happens if the node is null, was not + originally parsed from a stream, or has changed in a significant way), the + function returns -1. Otherwise it returns the offset to node's data from + the beginning of XML buffer in pugi::char_t + units. For more information on parsing offsets, see parsing + error handling documentation. +

+
+
+ + + +
+
+ + + +
+pugixml 1.0 manual | + Overview | + Installation | + Document: + Object model · Loading · Accessing · Modifying · Saving | + XPath | + API Reference | + Table of Contents +
+PrevUpHomeNext +
+ + diff --git a/LifeGraphics/pugixml/docs/manual/apiref.html b/LifeGraphics/pugixml/docs/manual/apiref.html new file mode 100644 index 0000000..5737c51 --- /dev/null +++ b/LifeGraphics/pugixml/docs/manual/apiref.html @@ -0,0 +1,1396 @@ + + + +API Reference + + + + + + + + + + + +
+pugixml 1.0 manual | + Overview | + Installation | + Document: + Object model · Loading · Accessing · Modifying · Saving | + XPath | + API Reference | + Table of Contents +
+PrevUpHomeNext +
+
+
+ +

+ This is the reference for all macros, types, enumerations, classes and functions + in pugixml. Each symbol is a link that leads to the relevant section of the + manual. +

+

+ Macros: +

+
+

+ Types: +

+
+

+ Enumerations: +

+ +

+ Constants: +

+
+

+ Classes: +

+
    +
  • + class xml_attribute +
    +
  • +
  • + class xml_node +
      +
    • + xml_node(); +

      + +
    • +
    • + bool empty() const; +
    • +
    • + operator unspecified_bool_type() 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; +
    • +
    • + bool operator>=(const xml_node& + r) + const; +

      + +
    • +
    • + size_t hash_value() const;

      + +
    • +
    • + xml_node_type type() + const; +

      + +
    • +
    • + const char_t* name() const; +
    • +
    • + const char_t* value() const;

      + +
    • +
    • + xml_node parent() const; +
    • +
    • + xml_node first_child() const; +
    • +
    • + xml_node last_child() const; +
    • +
    • + xml_node next_sibling() const; +
    • +
    • + xml_node previous_sibling() const;

      + +
    • +
    • + xml_attribute first_attribute() const; +
    • +
    • + xml_attribute last_attribute() const;

      + +
    • +
    • + 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; +
    • +
    • + 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;

      + +
    • +
    • + const char_t* child_value() const; +
    • +
    • + const char_t* child_value(const char_t* + name) + const; +

      + +
    • +
    • + typedef xml_node_iterator + iterator; +
    • +
    • + iterator begin() const; +
    • +
    • + iterator end() const;

      + +
    • +
    • + typedef xml_attribute_iterator + attribute_iterator; +
    • +
    • + attribute_iterator attributes_begin() const; +
    • +
    • + attribute_iterator attributes_end() const;

      + +
    • +
    • + bool traverse(xml_tree_walker& walker);

      + +
    • +
    • + template <typename Predicate> xml_attribute + find_attribute(Predicate + pred) + const; +
    • +
    • + template <typename Predicate> xml_node + find_child(Predicate + pred) + const; +
    • +
    • + template <typename Predicate> xml_node + find_node(Predicate + pred) + const; +

      + +
    • +
    • + string_t path(char_t + delimiter = + '/') + const; +
    • +
    • + xml_node xml_node::first_element_by_path(const char_t* + path, + char_t delimiter + = '/') const; +
    • +
    • + xml_node root() const; +
    • +
    • + ptrdiff_t offset_debug() const;

      + +
    • +
    • + bool set_name(const char_t* + rhs); +
    • +
    • + bool set_value(const char_t* + rhs); +

      + +
    • +
    • + 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);

      + +
    • +
    • + 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);

      + +
    • +
    • + 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);

      + +
    • +
    • + 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);

      + +
    • +
    • + 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);

      + +
    • +
    • + bool remove_attribute(const xml_attribute& + a); +
    • +
    • + bool remove_attribute(const char_t* + name); +
    • +
    • + bool remove_child(const xml_node& + n); +
    • +
    • + bool remove_child(const char_t* + name); +

      + +
    • +
    • + void print(xml_writer& writer, const + char_t* + indent = + "\t", + unsigned int + flags = + format_default, + xml_encoding encoding + = encoding_auto, unsigned + int depth + = 0) const; +
    • +
    • + void print(std::ostream& os, const + char_t* + indent = + "\t", + unsigned int + flags = + format_default, + xml_encoding encoding + = encoding_auto, unsigned + int depth + = 0) const; +
    • +
    • + void print(std::wostream& os, const + char_t* + indent = + "\t", + unsigned int + flags = + format_default, + unsigned int + depth = + 0) + const; +

      + +
    • +
    • + xpath_node select_single_node(const char_t* + query, + xpath_variable_set* + variables = + 0) + const; +
    • +
    • + xpath_node select_single_node(const xpath_query& + query) + const; +
    • +
    • + 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; +

      + +
    • +
    +
  • +
  • + class xml_document +
      +
    • + xml_document(); +
    • +
    • + ~xml_document();

      + +
    • +
    • + void reset(); +
    • +
    • + void reset(const xml_document& + proto); +

      + +
    • +
    • + xml_parse_result load(std::istream& + stream, + unsigned int + options = + parse_default, + xml_encoding encoding + = encoding_auto); +
    • +
    • + xml_parse_result load(std::wistream& + stream, + unsigned int + options = + parse_default); +

      + +
    • +
    • + xml_parse_result load(const char_t* contents, unsigned + int options + = parse_default);

      + +
    • +
    • + 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);

      + +
    • +
    • + xml_parse_result load_buffer(const void* contents, + size_t size, unsigned + int options + = parse_default, xml_encoding + encoding = + encoding_auto); +
    • +
    • + xml_parse_result load_buffer_inplace(void* contents, size_t + size, + unsigned int + options = + parse_default, + xml_encoding encoding + = encoding_auto); +
    • +
    • + xml_parse_result load_buffer_inplace_own(void* contents, size_t + size, + unsigned int + options = + parse_default, + xml_encoding encoding + = encoding_auto);

      + +
    • +
    • + bool save_file(const char* path, + const char_t* indent + = "\t", unsigned + int flags + = format_default, xml_encoding + encoding = + encoding_auto) + const; +
    • +
    • + bool save_file(const wchar_t* + path, + const char_t* indent + = "\t", unsigned + int flags + = format_default, xml_encoding + encoding = + encoding_auto) + const; +

      + +
    • +
    • + void save(std::ostream& stream, const + char_t* + indent = + "\t", + unsigned int + flags = + format_default, + xml_encoding encoding + = encoding_auto) const; +
    • +
    • + void save(std::wostream& stream, const + char_t* + indent = + "\t", + unsigned int + flags = + format_default) + const; +

      + +
    • +
    • + void save(xml_writer& writer, const + char_t* + indent = + "\t", + unsigned int + flags = + format_default, + xml_encoding encoding + = encoding_auto) const;

      + +
    • +
    • + xml_node document_element() const;

      + +
    • +
    +
  • +
  • + struct xml_parse_result +
    +
  • +
  • + class xml_node_iterator +
  • +
  • + class xml_attribute_iterator +

    + +
  • +
  • + class xml_tree_walker +
      +
    • + virtual bool + begin(xml_node& node); +
    • +
    • + virtual bool + for_each(xml_node& node) = 0; +
    • +
    • + virtual bool + end(xml_node& node);

      + +
    • +
    • + int depth() const;

      + +
    • +
    +
  • +
  • + class xml_writer +
    • + virtual void + write(const void* data, + size_t size) = 0; +

      + +
    +
  • +
  • + class xml_writer_file: public xml_writer +
    +
  • +
  • + class xml_writer_stream: public xml_writer +
    +
  • +
  • + struct xpath_parse_result +
    +
  • +
  • + class xpath_query +
    +
  • +
  • + class xpath_exception: public std::exception +
      +
    • + virtual const + char* + what() const + throw(); +

      + +
    • +
    • + const xpath_parse_result& result() const;

      + +
    • +
    +
  • +
  • + class xpath_node +
    +
  • +
  • + class xpath_node_set +
    +
  • +
  • + class xpath_variable +
      +
    • + const char_t* name() const; +
    • +
    • + xpath_value_type type() + const; +

      + +
    • +
    • + bool get_boolean() const; +
    • +
    • + double get_number() const; +
    • +
    • + const char_t* get_string() const; +
    • +
    • + const xpath_node_set& get_node_set() const;

      + +
    • +
    • + bool set(bool value); +
    • +
    • + bool set(double + value); +
    • +
    • + bool set(const char_t* + value); +
    • +
    • + bool set(const xpath_node_set& + value); +

      + +
    • +
    +
  • +
  • + class xpath_variable_set +
      +
    • + xpath_variable* + add(const char_t* + name, + xpath_value_type type);

      + +
    • +
    • + 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);

      + +
    • +
    • + xpath_variable* + get(const char_t* + name); +
    • +
    • + const xpath_variable* get(const char_t* + name) + const; +

      + +
    • +
    +
  • +
+

+ Functions: +

+
+
+ + + +
+
+ + + +
+pugixml 1.0 manual | + Overview | + Installation | + Document: + Object model · Loading · Accessing · Modifying · Saving | + XPath | + API Reference | + Table of Contents +
+PrevUpHomeNext +
+ + diff --git a/LifeGraphics/pugixml/docs/manual/changes.html b/LifeGraphics/pugixml/docs/manual/changes.html new file mode 100644 index 0000000..78cde23 --- /dev/null +++ b/LifeGraphics/pugixml/docs/manual/changes.html @@ -0,0 +1,785 @@ + + + +Changelog + + + + + + + + + + + +
+pugixml 1.0 manual | + Overview | + Installation | + Document: + Object model · Loading · Accessing · Modifying · Saving | + XPath | + API Reference | + Table of Contents +
+PrevUpHomeNext +
+
+
+ +
+ 1.11.2010 - version + 1.0 +
+

+ Major release, featuring many XPath enhancements, wide character filename support, + miscellaneous performance improvements, bug fixes and more. +

+
    +
  • + XPath: +
      +
    1. + XPath implementation is moved to pugixml.cpp (which is the only source + file now); use PUGIXML_NO_XPATH if you want to disable XPath to reduce + code size +
    2. +
    3. + XPath is now supported without exceptions (PUGIXML_NO_EXCEPTIONS); + the error handling mechanism depends on the presence of exception + support +
    4. +
    5. + XPath is now supported without STL (PUGIXML_NO_STL) +
    6. +
    7. + Introduced variable support +
    8. +
    9. + Introduced new xpath_query::evaluate_string, which works without + STL +
    10. +
    11. + Introduced new xpath_node_set constructor (from an iterator range) +
    12. +
    13. + Evaluation function now accept attribute context nodes +
    14. +
    15. + All internal allocations use custom allocation functions +
    16. +
    17. + Improved error reporting; now a last parsed offset is returned together + with the parsing error +
    18. +
    +
  • +
  • + Bug fixes: +
      +
    1. + Fixed memory leak for loading from streams with stream exceptions + turned on +
    2. +
    3. + Fixed custom deallocation function calling with null pointer in one + case +
    4. +
    5. + Fixed missing attributes for iterator category functions; all functions/classes + can now be DLL-exported +
    6. +
    7. + Worked around Digital Mars compiler bug, which lead to minor read + overfetches in several functions +
    8. +
    9. + load_file now works with 2+ Gb files in MSVC/MinGW +
    10. +
    11. + XPath: fixed memory leaks for incorrect queries +
    12. +
    13. + XPath: fixed xpath_node() attribute constructor with empty attribute + argument +
    14. +
    15. + XPath: fixed lang() function for non-ASCII arguments +
    16. +
    +
  • +
  • + Specification changes: +
      +
    1. + CDATA nodes containing ]]> are printed as several nodes; while + this changes the internal structure, this is the only way to escape + CDATA contents +
    2. +
    3. + Memory allocation errors during parsing now preserve last parsed + offset (to give an idea about parsing progress) +
    4. +
    5. + If an element node has the only child, and it is of CDATA type, then + the extra indentation is omitted (previously this behavior only held + for PCDATA children) +
    6. +
    +
  • +
  • + Additional functionality: +
      +
    1. + Added xml_parse_result default constructor +
    2. +
    3. + Added xml_document::load_file and xml_document::save_file with wide + character paths +
    4. +
    5. + Added as_utf8 and as_wide overloads for std::wstring/std::string + arguments +
    6. +
    7. + Added DOCTYPE node type (node_doctype) and a special parse flag, + parse_doctype, to add such nodes to the document during parsing +
    8. +
    9. + Added parse_full parse flag mask, which extends parse_default with + all node type parsing flags except parse_ws_pcdata +
    10. +
    11. + Added xml_node::hash_value() and xml_attribute::hash_value() functions + for use in hash-based containers +
    12. +
    13. + Added internal_object() and additional constructor for both xml_node + and xml_attribute for easier marshalling (useful for language bindings) +
    14. +
    15. + Added xml_document::document_element() function +
    16. +
    17. + Added xml_node::prepend_attribute, xml_node::prepend_child and xml_node::prepend_copy + functions +
    18. +
    19. + Added xml_node::append_child, xml_node::prepend_child, xml_node::insert_child_before + and xml_node::insert_child_after overloads for element nodes (with + name instead of type) +
    20. +
    21. + Added xml_document::reset() function +
    22. +
    +
  • +
  • + Performance improvements: +
      +
    1. + xml_node::root() and xml_node::offset_debug() are now O(1) instead + of O(logN) +
    2. +
    3. + Minor parsing optimizations +
    4. +
    5. + Minor memory optimization for strings in DOM tree (set_name/set_value) +
    6. +
    7. + Memory optimization for string memory reclaiming in DOM tree (set_name/set_value + now reallocate the buffer if memory waste is too big) +
    8. +
    9. + XPath: optimized document order sorting +
    10. +
    11. + XPath: optimized child/attribute axis step +
    12. +
    13. + XPath: optimized number-to-string conversions in MSVC +
    14. +
    15. + XPath: optimized concat for many arguments +
    16. +
    17. + XPath: optimized evaluation allocation mechanism: constant and document + strings are not heap-allocated +
    18. +
    19. + XPath: optimized evaluation allocation mechanism: all temporaries' + allocations use fast stack-like allocator +
    20. +
    +
  • +
  • + Compatibility: +
      +
    1. + Removed wildcard functions (xml_node::child_w, xml_node::attribute_w, + etc.) +
    2. +
    3. + Removed xml_node::all_elements_by_name +
    4. +
    5. + Removed xpath_type_t enumeration; use xpath_value_type instead +
    6. +
    7. + Removed format_write_bom_utf8 enumeration; use format_write_bom instead +
    8. +
    9. + Removed xml_document::precompute_document_order, xml_attribute::document_order + and xml_node::document_order functions; document order sort optimization + is now automatic +
    10. +
    11. + Removed xml_document::parse functions and transfer_ownership struct; + use xml_document::load_buffer_inplace and xml_document::load_buffer_inplace_own + instead +
    12. +
    13. + Removed as_utf16 function; use as_wide instead +
    14. +
    +
  • +
+
+ 1.07.2010 - version + 0.9 +
+

+ Major release, featuring extended and improved Unicode support, miscellaneous + performance improvements, bug fixes and more. +

+
    +
  • + Major Unicode improvements: +
      +
    1. + Introduced encoding support (automatic/manual encoding detection + on load, manual encoding selection on save, conversion from/to UTF8, + UTF16 LE/BE, UTF32 LE/BE) +
    2. +
    3. + Introduced wchar_t mode (you can set PUGIXML_WCHAR_MODE define to + switch pugixml internal encoding from UTF8 to wchar_t; all functions + are switched to their Unicode variants) +
    4. +
    5. + Load/save functions now support wide streams +
    6. +
    +
  • +
  • + Bug fixes: +
      +
    1. + Fixed document corruption on failed parsing bug +
    2. +
    3. + XPath string <-> number conversion improvements (increased + precision, fixed crash for huge numbers) +
    4. +
    5. + Improved DOCTYPE parsing: now parser recognizes all well-formed DOCTYPE + declarations +
    6. +
    7. + Fixed xml_attribute::as_uint() for large numbers (i.e. 2^32-1) +
    8. +
    9. + Fixed xml_node::first_element_by_path for path components that are + prefixes of node names, but are not exactly equal to them. +
    10. +
    +
  • +
  • + Specification changes: +
      +
    1. + parse() API changed to load_buffer/load_buffer_inplace/load_buffer_inplace_own; + load_buffer APIs do not require zero-terminated strings. +
    2. +
    3. + Renamed as_utf16 to as_wide +
    4. +
    5. + Changed xml_node::offset_debug return type and xml_parse_result::offset + type to ptrdiff_t +
    6. +
    7. + Nodes/attributes with empty names are now printed as :anonymous +
    8. +
    +
  • +
  • + Performance improvements: +
      +
    1. + Optimized document parsing and saving +
    2. +
    3. + Changed internal memory management: internal allocator is used for + both metadata and name/value data; allocated pages are deleted if + all allocations from them are deleted +
    4. +
    5. + Optimized memory consumption: sizeof(xml_node_struct) reduced from + 40 bytes to 32 bytes on x86 +
    6. +
    7. + Optimized debug mode parsing/saving by order of magnitude +
    8. +
    +
  • +
  • + Miscellaneous: +
      +
    1. + All STL includes except <exception> in pugixml.hpp are replaced + with forward declarations +
    2. +
    3. + xml_node::remove_child and xml_node::remove_attribute now return + the operation result +
    4. +
    +
  • +
  • + Compatibility: +
      +
    1. + parse() and as_utf16 are left for compatibility (these functions + are deprecated and will be removed in version 1.0) +
    2. +
    3. + Wildcard functions, document_order/precompute_document_order functions, + all_elements_by_name function and format_write_bom_utf8 flag are + deprecated and will be removed in version 1.0 +
    4. +
    5. + xpath_type_t enumeration was renamed to xpath_value_type; xpath_type_t + is deprecated and will be removed in version 1.0 +
    6. +
    +
  • +
+
+ 8.11.2009 - version + 0.5 +
+

+ Major bugfix release. Changes: +

+
    +
  • + XPath bugfixes: +
      +
    1. + Fixed translate(), lang() and concat() functions (infinite loops/crashes) +
    2. +
    3. + Fixed compilation of queries with empty literal strings ("") +
    4. +
    5. + Fixed axis tests: they never add empty nodes/attributes to the resulting + node set now +
    6. +
    7. + Fixed string-value evaluation for node-set (the result excluded some + text descendants) +
    8. +
    9. + Fixed self:: axis (it behaved like ancestor-or-self::) +
    10. +
    11. + Fixed following:: and preceding:: axes (they included descendent + and ancestor nodes, respectively) +
    12. +
    13. + Minor fix for namespace-uri() function (namespace declaration scope + includes the parent element of namespace declaration attribute) +
    14. +
    15. + Some incorrect queries are no longer parsed now (i.e. foo: *) +
    16. +
    17. + Fixed text()/etc. node test parsing bug (i.e. foo[text()] failed + to compile) +
    18. +
    19. + Fixed root step (/) - it now selects empty node set if query is evaluated + on empty node +
    20. +
    21. + Fixed string to number conversion ("123 " converted to + NaN, "123 .456" converted to 123.456 - now the results + are 123 and NaN, respectively) +
    22. +
    23. + Node set copying now preserves sorted type; leads to better performance + on some queries +
    24. +
    +
  • +
  • + Miscellaneous bugfixes: +
      +
    1. + Fixed xml_node::offset_debug for PI nodes +
    2. +
    3. + Added empty attribute checks to xml_node::remove_attribute +
    4. +
    5. + Fixed node_pi and node_declaration copying +
    6. +
    7. + Const-correctness fixes +
    8. +
    +
  • +
  • + Specification changes: +
      +
    1. + xpath_node::select_nodes() and related functions now throw exception + if expression return type is not node set (instead of assertion) +
    2. +
    3. + xml_node::traverse() now sets depth to -1 for both begin() and end() + callbacks (was 0 at begin() and -1 at end()) +
    4. +
    5. + In case of non-raw node printing a newline is output after PCDATA + inside nodes if the PCDATA has siblings +
    6. +
    7. + UTF8 -> wchar_t conversion now considers 5-byte UTF8-like sequences + as invalid +
    8. +
    +
  • +
  • + New features: +
      +
    1. + Added xpath_node_set::operator[] for index-based iteration +
    2. +
    3. + Added xpath_query::return_type() +
    4. +
    5. + Added getter accessors for memory-management functions +
    6. +
    +
  • +
+
+ 17.09.2009 - version + 0.42 +
+

+ Maintenance release. Changes: +

+
    +
  • + Bug fixes: +
      +
    1. + Fixed deallocation in case of custom allocation functions or if delete[] + / free are incompatible +
    2. +
    3. + XPath parser fixed for incorrect queries (i.e. incorrect XPath queries + should now always fail to compile) +
    4. +
    5. + Const-correctness fixes for find_child_by_attribute +
    6. +
    7. + Improved compatibility (miscellaneous warning fixes, fixed cstring + include dependency for GCC) +
    8. +
    9. + Fixed iterator begin/end and print function to work correctly for + empty nodes +
    10. +
    +
  • +
  • + New features: +
      +
    1. + Added PUGIXML_API/PUGIXML_CLASS/PUGIXML_FUNCTION configuration macros + to control class/function attributes +
    2. +
    3. + Added xml_attribute::set_value overloads for different types +
    4. +
    +
  • +
+
+ 8.02.2009 - version + 0.41 +
+

+ Maintenance release. Changes: +

+
  • + Bug fixes: +
    1. + Fixed bug with node printing (occasionally some content was not written + to output stream) +
    +
+
+ 18.01.2009 - version + 0.4 +
+

+ Changes: +

+
    +
  • + Bug fixes: +
      +
    1. + Documentation fix in samples for parse() with manual lifetime control +
    2. +
    3. + Fixed document order sorting in XPath (it caused wrong order of nodes + after xpath_node_set::sort and wrong results of some XPath queries) +
    4. +
    +
  • +
  • + Node printing changes: +
      +
    1. + Single quotes are no longer escaped when printing nodes +
    2. +
    3. + Symbols in second half of ASCII table are no longer escaped when + printing nodes; because of this, format_utf8 flag is deleted as it's + no longer needed and format_write_bom is renamed to format_write_bom_utf8. +
    4. +
    5. + Reworked node printing - now it works via xml_writer interface; implementations + for FILE* and std::ostream are available. As a side-effect, xml_document::save_file + now works without STL. +
    6. +
    +
  • +
  • + New features: +
      +
    1. + Added unsigned integer support for attributes (xml_attribute::as_uint, + xml_attribute::operator=) +
    2. +
    3. + Now document declaration (<?xml ...?>) is parsed as node with + type node_declaration when parse_declaration flag is specified (access + to encoding/version is performed as if they were attributes, i.e. + doc.child("xml").attribute("version").as_float()); + corresponding flags for node printing were also added +
    4. +
    5. + Added support for custom memory management (see set_memory_management_functions + for details) +
    6. +
    7. + Implemented node/attribute copying (see xml_node::insert_copy_* and + xml_node::append_copy for details) +
    8. +
    9. + Added find_child_by_attribute and find_child_by_attribute_w to simplify + parsing code in some cases (i.e. COLLADA files) +
    10. +
    11. + Added file offset information querying for debugging purposes (now + you're able to determine exact location of any xml_node in parsed + file, see xml_node::offset_debug for details) +
    12. +
    13. + Improved error handling for parsing - now load(), load_file() and + parse() return xml_parse_result, which contains error code and last + parsed offset; this does not break old interface as xml_parse_result + can be implicitly casted to bool. +
    14. +
    +
  • +
+
+ 31.10.2007 - version + 0.34 +
+

+ Maintenance release. Changes: +

+
    +
  • + Bug fixes: +
      +
    1. + Fixed bug with loading from text-mode iostreams +
    2. +
    3. + Fixed leak when transfer_ownership is true and parsing is failing +
    4. +
    5. + Fixed bug in saving (\r and \n are now escaped in attribute values) +
    6. +
    7. + Renamed free() to destroy() - some macro conflicts were reported +
    8. +
    +
  • +
  • + New features: +
      +
    1. + Improved compatibility (supported Digital Mars C++, MSVC 6, CodeWarrior + 8, PGI C++, Comeau, supported PS3 and XBox360) +
    2. +
    3. + PUGIXML_NO_EXCEPTION flag for platforms without exception handling +
    4. +
    +
  • +
+
+ 21.02.2007 - version + 0.3 +
+

+ Refactored, reworked and improved version. Changes: +

+
    +
  • + Interface: +
      +
    1. + Added XPath +
    2. +
    3. + Added tree modification functions +
    4. +
    5. + Added no STL compilation mode +
    6. +
    7. + Added saving document to file +
    8. +
    9. + Refactored parsing flags +
    10. +
    11. + Removed xml_parser class in favor of xml_document +
    12. +
    13. + Added transfer ownership parsing mode +
    14. +
    15. + Modified the way xml_tree_walker works +
    16. +
    17. + Iterators are now non-constant +
    18. +
    +
  • +
  • + Implementation: +
      +
    1. + Support of several compilers and platforms +
    2. +
    3. + Refactored and sped up parsing core +
    4. +
    5. + Improved standard compliancy +
    6. +
    7. + Added XPath implementation +
    8. +
    9. + Fixed several bugs +
    10. +
    +
  • +
+
+ 6.11.2006 - version + 0.2 +
+

+ First public release. Changes: +

+
    +
  • + Bug fixes: +
      +
    1. + Fixed child_value() (for empty nodes) +
    2. +
    3. + Fixed xml_parser_impl warning at W4 +
    4. +
    +
  • +
  • + New features: +
      +
    1. + Introduced child_value(name) and child_value_w(name) +
    2. +
    3. + parse_eol_pcdata and parse_eol_attribute flags + parse_minimal optimizations +
    4. +
    5. + Optimizations of strconv_t +
    6. +
    +
  • +
+
+ 15.07.2006 - version + 0.1 +
+

+ First private release for testing purposes +

+
+ + + +
+
+ + + +
+pugixml 1.0 manual | + Overview | + Installation | + Document: + Object model · Loading · Accessing · Modifying · Saving | + XPath | + API Reference | + Table of Contents +
+PrevUpHomeNext +
+ + diff --git a/LifeGraphics/pugixml/docs/manual/dom.html b/LifeGraphics/pugixml/docs/manual/dom.html new file mode 100644 index 0000000..22509a9 --- /dev/null +++ b/LifeGraphics/pugixml/docs/manual/dom.html @@ -0,0 +1,682 @@ + + + +Document object model + + + + + + + + + + + +
+pugixml 1.0 manual | + Overview | + Installation | + Document: + Object model · Loading · Accessing · Modifying · Saving | + XPath | + API Reference | + Table of Contents +
+PrevUpHomeNext +
+
+
+ + +

+ pugixml stores XML data in DOM-like way: the entire XML document (both document + structure and element data) is stored in memory as a tree. The tree can be + loaded from a character stream (file, string, C++ I/O stream), then traversed + with the special API or XPath expressions. The whole tree is mutable: both + node structure and node/attribute data can be changed at any time. Finally, + the result of document transformations can be saved to a character stream (file, + C++ I/O stream or custom transport). +

+
+ +

+ The XML document is represented with a tree data structure. The root of the + tree is the document itself, which corresponds to C++ type xml_document. + Document has one or more child nodes, which correspond to C++ type xml_node. Nodes have different types; depending + on a type, a node can have a collection of child nodes, a collection of attributes, + which correspond to C++ type xml_attribute, + and some additional data (i.e. name). +

+

+ The tree nodes can be of one of the following types (which together form + the enumeration xml_node_type): +

+
    +
  • + Document node (node_document) - this + is the root of the tree, which consists of several child nodes. This + node corresponds to xml_document + class; note that xml_document is + a sub-class of xml_node, so the entire + node interface is also available. However, document node is special in + several ways, which are covered below. There can be only one document + node in the tree; document node does not have any XML representation. +

    + +
  • +
  • + Element/tag node (node_element) - this + is the most common type of node, which represents XML elements. Element + nodes have a name, a collection of attributes and a collection of child + nodes (both of which may be empty). The attribute is a simple name/value + pair. The example XML representation of element nodes is as follows: +
  • +
+
<node attr="value"><child/></node>
+
+

+ There are two element nodes here: one has name "node", + single attribute "attr" + and single child "child", + another has name "child" + and does not have any attributes or child nodes. +

+
  • + Plain character data nodes (node_pcdata) + represent plain text in XML. PCDATA nodes have a value, but do not have + a name or children/attributes. Note that plain character data is not + a part of the element node but instead has its own node; for example, + an element node can have several child PCDATA nodes. The example XML + representation of text nodes is as follows: +
+
<node> text1 <child/> text2 </node>
+
+

+ Here "node" element + has three children, two of which are PCDATA nodes with values "text1" and "text2". +

+
  • + Character data nodes (node_cdata) represent + text in XML that is quoted in a special way. CDATA nodes do not differ + from PCDATA nodes except in XML representation - the above text example + looks like this with CDATA: +
+
<node> <![CDATA[[text1]]> <child/> <![CDATA[[text2]]> </node>
+
+

+ CDATA nodes make it easy to include non-escaped <, & and > characters + in plain text. CDATA value can not contain the character sequence ]]>, + since it is used to determine the end of node contents. +

+
  • + Comment nodes (node_comment) represent + comments in XML. Comment nodes have a value, but do not have a name or + children/attributes. The example XML representation of a comment node + is as follows: +
+
<!-- comment text -->
+
+

+ Here the comment node has value "comment + text". By default comment nodes are treated as non-essential + part of XML markup and are not loaded during XML parsing. You can override + this behavior with parse_comments + flag. +

+
  • + Processing instruction node (node_pi) represent + processing instructions (PI) in XML. PI nodes have a name and an optional + value, but do not have children/attributes. The example XML representation + of a PI node is as follows: +
+
<?name value?>
+
+

+ Here the name (also called PI target) is "name", + and the value is "value". + By default PI nodes are treated as non-essential part of XML markup and + are not loaded during XML parsing. You can override this behavior with + parse_pi flag. +

+
  • + Declaration node (node_declaration) + represents document declarations in XML. Declaration nodes have a name + ("xml") and an + optional collection of attributes, but do not have value or children. + There can be only one declaration node in a document; moreover, it should + be the topmost node (its parent should be the document). The example + XML representation of a declaration node is as follows: +
+
<?xml version="1.0"?>
+
+

+ Here the node has name "xml" + and a single attribute with name "version" + and value "1.0". + By default declaration nodes are treated as non-essential part of XML markup + and are not loaded during XML parsing. You can override this behavior with + parse_declaration flag. Also, + by default a dummy declaration is output when XML document is saved unless + there is already a declaration in the document; you can disable this with + format_no_declaration flag. +

+
  • + Document type declaration node (node_doctype) + represents document type declarations in XML. Document type declaration + nodes have a value, which corresponds to the entire document type contents; + no additional nodes are created for inner elements like <!ENTITY>. There can be only one document type + declaration node in a document; moreover, it should be the topmost node + (its parent should be the document). The example XML representation of + a document type declaration node is as follows: +
+
<!DOCTYPE greeting [ <!ELEMENT greeting (#PCDATA)> ]>
+
+

+ Here the node has value "greeting [ <!ELEMENT + greeting (#PCDATA)> ]". By default document type + declaration nodes are treated as non-essential part of XML markup and are + not loaded during XML parsing. You can override this behavior with parse_doctype flag. +

+

+ Finally, here is a complete example of XML document and the corresponding + tree representation (samples/tree.xml): +

+
++++ + + + + +
+

+ +

+
<?xml version="1.0"?>
+<mesh name="mesh_root">
+    <!-- here is a mesh node -->
+    some text
+    <![CDATA[someothertext]]>
+    some more text
+    <node attr1="value1" attr2="value2" />
+    <node attr1="value2">
+        <innernode/>
+    </node>
+</mesh>
+<?include somedata?>
+
+

+

+
+

+ dom_tree_thumb +

+
+
+
+ +
+ + + + + +
[Note]Note

+ All pugixml classes and functions are located in the pugi + namespace; you have to either use explicit name qualification (i.e. pugi::xml_node), or to gain access to relevant + symbols via using directive + (i.e. using pugi::xml_node; or using + namespace pugi;). The namespace will be omitted from all + declarations in this documentation hereafter; all code examples will use + fully qualified names. +

+

+ Despite the fact that there are several node types, there are only three + C++ classes representing the tree (xml_document, + xml_node, xml_attribute); + some operations on xml_node + are only valid for certain node types. The classes are described below. +

+

+ xml_document is the owner + of the entire document structure; it is a non-copyable class. The interface + of xml_document consists + of loading functions (see Loading document), saving functions (see Saving document) + and the entire interface of xml_node, + which allows for document inspection and/or modification. Note that while + xml_document is a sub-class + of xml_node, xml_node is not a polymorphic type; the + inheritance is present only to simplify usage. Alternatively you can use + the document_element function + to get the element node that's the immediate child of the document. +

+

+ Default constructor of xml_document + initializes the document to the tree with only a root node (document node). + You can then populate it with data using either tree modification functions + or loading functions; all loading functions destroy the previous tree with + all occupied memory, which puts existing node/attribute handles for this + document to invalid state. If you want to destroy the previous tree, you + can use the xml_document::reset + function; it destroys the tree and replaces it with either an empty one or + a copy of the specified document. Destructor of xml_document + also destroys the tree, thus the lifetime of the document object should exceed + the lifetimes of any node/attribute handles that point to the tree. +

+
+ + + + + +
[Caution]Caution

+ While technically node/attribute handles can be alive when the tree they're + referring to is destroyed, calling any member function for these handles + results in undefined behavior. Thus it is recommended to make sure that + the document is destroyed only after all references to its nodes/attributes + are destroyed. +

+

+ xml_node is the handle to + document node; it can point to any node in the document, including the document + node itself. There is a common interface for nodes of all types; the actual + node type can be queried via the xml_node::type() + method. Note that xml_node + is only a handle to the actual node, not the node itself - you can have several + xml_node handles pointing + to the same underlying object. Destroying xml_node + handle does not destroy the node and does not remove it from the tree. The + size of xml_node is equal + to that of a pointer, so it is nothing more than a lightweight wrapper around + a pointer; you can safely pass or return xml_node + objects by value without additional overhead. +

+

+ There is a special value of xml_node + type, known as null node or empty node (such nodes have type node_null). It does not correspond to any + node in any document, and thus resembles null pointer. However, all operations + are defined on empty nodes; generally the operations don't do anything and + return empty nodes/attributes or empty strings as their result (see documentation + for specific functions for more detailed information). This is useful for + chaining calls; i.e. you can get the grandparent of a node like so: node.parent().parent(); if a node is a null node or it does not + have a parent, the first parent() call returns null node; the second parent() + call then also returns null node, which makes error handling easier. +

+

+ xml_attribute is the handle + to an XML attribute; it has the same semantics as xml_node, + i.e. there can be several xml_attribute + handles pointing to the same underlying object and there is a special null + attribute value, which propagates to function results. +

+

+ Both xml_node and xml_attribute have the default constructor + which initializes them to null objects. +

+

+ xml_node and xml_attribute try to behave like pointers, + that is, they can be compared with other objects of the same type, making + it possible to use them as keys in associative containers. All handles to + the same underlying object are equal, and any two handles to different underlying + objects are not equal. Null handles only compare as equal to themselves. + The result of relational comparison can not be reliably determined from the + order of nodes in file or in any other way. Do not use relational comparison + operators except for search optimization (i.e. associative container keys). +

+

+ If you want to use xml_node + or xml_attribute objects + as keys in hash-based associative containers, you can use the hash_value member functions. They return + the hash values that are guaranteed to be the same for all handles to the + same underlying object. The hash value for null handles is 0. +

+

+ Finally handles can be implicitly cast to boolean-like objects, so that you + can test if the node/attribute is empty with the following code: if (node) { ... + } or if + (!node) { ... + } else { ... }. + Alternatively you can check if a given xml_node/xml_attribute handle is null by calling + the following methods: +

+
bool xml_attribute::empty() const;
+bool xml_node::empty() const;
+
+

+ Nodes and attributes do not exist without a document tree, so you can't create + them without adding them to some document. Once underlying node/attribute + objects are destroyed, the handles to those objects become invalid. While + this means that destruction of the entire tree invalidates all node/attribute + handles, it also means that destroying a subtree (by calling xml_node::remove_child) + or removing an attribute invalidates the corresponding handles. There is + no way to check handle validity; you have to ensure correctness through external + mechanisms. +

+
+
+ +

+ There are two choices of interface and internal representation when configuring + pugixml: you can either choose the UTF-8 (also called char) interface or + UTF-16/32 (also called wchar_t) one. The choice is controlled via PUGIXML_WCHAR_MODE + define; you can set it via pugiconfig.hpp or via preprocessor options, as + discussed in Additional configuration + options. If this define is set, the wchar_t + interface is used; otherwise (by default) the char interface is used. The + exact wide character encoding is assumed to be either UTF-16 or UTF-32 and + is determined based on the size of wchar_t + type. +

+
+ + + + + +
[Note]Note

+ If the size of wchar_t is + 2, pugixml assumes UTF-16 encoding instead of UCS-2, which means that some + characters are represented as two code points. +

+

+ All tree functions that work with strings work with either C-style null terminated + strings or STL strings of the selected character type. For example, node + name accessors look like this in char mode: +

+
const char* xml_node::name() const;
+bool xml_node::set_name(const char* value);
+
+

+ and like this in wchar_t mode: +

+
const wchar_t* xml_node::name() const;
+bool xml_node::set_name(const wchar_t* value);
+
+

+ There is a special type, pugi::char_t, + that is defined as the character type and depends on the library configuration; + it will be also used in the documentation hereafter. There is also a type + pugi::string_t, which is defined as the STL string + of the character type; it corresponds to std::string + in char mode and to std::wstring in wchar_t mode. +

+

+ In addition to the interface, the internal implementation changes to store + XML data as pugi::char_t; this means that these two modes + have different memory usage characteristics. The conversion to pugi::char_t upon document loading and from + pugi::char_t upon document saving happen automatically, + which also carries minor performance penalty. The general advice however + is to select the character mode based on usage scenario, i.e. if UTF-8 is + inconvenient to process and most of your XML data is non-ASCII, wchar_t mode + is probably a better choice. +

+

+ There are cases when you'll have to convert string data between UTF-8 and + wchar_t encodings; the following helper functions are provided for such purposes: +

+
std::string as_utf8(const wchar_t* str);
+std::wstring as_wide(const char* str);
+
+

+ Both functions accept a null-terminated string as an argument str, and return the converted string. + as_utf8 performs conversion + from UTF-16/32 to UTF-8; as_wide + performs conversion from UTF-8 to UTF-16/32. Invalid UTF sequences are silently + discarded upon conversion. str + has to be a valid string; passing null pointer results in undefined behavior. + There are also two overloads with the same semantics which accept a string + as an argument: +

+
std::string as_utf8(const std::wstring& str);
+std::wstring as_wide(const std::string& str);
+
+
+ + + + + +
[Note]Note
+

+ Most examples in this documentation assume char interface and therefore + will not compile with PUGIXML_WCHAR_MODE. + This is done to simplify the documentation; usually the only changes you'll + have to make is to pass wchar_t + string literals, i.e. instead of +

+

+ pugi::xml_node node + = doc.child("bookstore").find_child_by_attribute("book", "id", "12345"); +

+

+ you'll have to do +

+

+ pugi::xml_node node + = doc.child(L"bookstore").find_child_by_attribute(L"book", L"id", L"12345"); +

+
+
+
+ +

+ Almost all functions in pugixml have the following thread-safety guarantees: +

+
    +
  • + it is safe to call free (non-member) functions from multiple threads +
  • +
  • + it is safe to perform concurrent read-only accesses to the same tree + (all constant member functions do not modify the tree) +
  • +
  • + it is safe to perform concurrent read/write accesses, if there is only + one read or write access to the single tree at a time +
  • +
+

+ Concurrent modification and traversing of a single tree requires synchronization, + for example via reader-writer lock. Modification includes altering document + structure and altering individual node/attribute data, i.e. changing names/values. +

+

+ The only exception is set_memory_management_functions; + it modifies global variables and as such is not thread-safe. Its usage policy + has more restrictions, see Custom memory allocation/deallocation + functions. +

+
+
+ +

+ With the exception of XPath, pugixml itself does not throw any exceptions. + Additionally, most pugixml functions have a no-throw exception guarantee. +

+

+ This is not applicable to functions that operate on STL strings or IOstreams; + such functions have either strong guarantee (functions that operate on strings) + or basic guarantee (functions that operate on streams). Also functions that + call user-defined callbacks (i.e. xml_node::traverse + or xml_node::find_node) do not + provide any exception guarantees beyond the ones provided by the callback. +

+

+ If exception handling is not disabled with PUGIXML_NO_EXCEPTIONS + define, XPath functions may throw xpath_exception + on parsing errors; also, XPath functions may throw std::bad_alloc + in low memory conditions. Still, XPath functions provide strong exception + guarantee. +

+
+
+ +

+ pugixml requests the memory needed for document storage in big chunks, and + allocates document data inside those chunks. This section discusses replacing + functions used for chunk allocation and internal memory management implementation. +

+
+ +

+ All memory for tree structure, tree data and XPath objects is allocated + via globally specified functions, which default to malloc/free. You can + set your own allocation functions with set_memory_management function. + The function interfaces are the same as that of malloc/free: +

+
typedef void* (*allocation_function)(size_t size);
+typedef void (*deallocation_function)(void* ptr);
+
+

+ You can use the following accessor functions to change or get current memory + management functions: +

+
void set_memory_management_functions(allocation_function allocate, deallocation_function deallocate);
+allocation_function get_memory_allocation_function();
+deallocation_function get_memory_deallocation_function();
+
+

+ Allocation function is called with the size (in bytes) as an argument and + should return a pointer to a memory block with alignment that is suitable + for storage of primitive types (usually a maximum of void* and double + types alignment is sufficient) and size that is greater than or equal to + the requested one. If the allocation fails, the function has to return + null pointer (throwing an exception from allocation function results in + undefined behavior). +

+

+ Deallocation function is called with the pointer that was returned by some + call to allocation function; it is never called with a null pointer. If + memory management functions are not thread-safe, library thread safety + is not guaranteed. +

+

+ This is a simple example of custom memory management (samples/custom_memory_management.cpp): +

+

+ +

+
void* custom_allocate(size_t size)
+{
+    return new (std::nothrow) char[size];
+}
+
+void custom_deallocate(void* ptr)
+{
+    delete[] static_cast<char*>(ptr);
+}
+
+

+

+

+ +

+
pugi::set_memory_management_functions(custom_allocate, custom_deallocate);
+
+

+

+

+ When setting new memory management functions, care must be taken to make + sure that there are no live pugixml objects. Otherwise when the objects + are destroyed, the new deallocation function will be called with the memory + obtained by the old allocation function, resulting in undefined behavior. +

+
+
+ +

+ Constructing a document object using the default constructor does not result + in any allocations; document node is stored inside the xml_document + object. +

+

+ When the document is loaded from file/buffer, unless an inplace loading + function is used (see Loading document from memory), a complete copy of character + stream is made; all names/values of nodes and attributes are allocated + in this buffer. This buffer is allocated via a single large allocation + and is only freed when document memory is reclaimed (i.e. if the xml_document object is destroyed or if another + document is loaded in the same object). Also when loading from file or + stream, an additional large allocation may be performed if encoding conversion + is required; a temporary buffer is allocated, and it is freed before load + function returns. +

+

+ All additional memory, such as memory for document structure (node/attribute + objects) and memory for node/attribute names/values is allocated in pages + on the order of 32 kilobytes; actual objects are allocated inside the pages + using a memory management scheme optimized for fast allocation/deallocation + of many small objects. Because of the scheme specifics, the pages are only + destroyed if all objects inside them are destroyed; also, generally destroying + an object does not mean that subsequent object creation will reuse the + same memory. This means that it is possible to devise a usage scheme which + will lead to higher memory usage than expected; one example is adding a + lot of nodes, and them removing all even numbered ones; not a single page + is reclaimed in the process. However this is an example specifically crafted + to produce unsatisfying behavior; in all practical usage scenarios the + memory consumption is less than that of a general-purpose allocator because + allocation meta-data is very small in size. +

+
+
+
+ + + +
+
+ + + +
+pugixml 1.0 manual | + Overview | + Installation | + Document: + Object model · Loading · Accessing · Modifying · Saving | + XPath | + API Reference | + Table of Contents +
+PrevUpHomeNext +
+ + diff --git a/LifeGraphics/pugixml/docs/manual/install.html b/LifeGraphics/pugixml/docs/manual/install.html new file mode 100644 index 0000000..9809a39 --- /dev/null +++ b/LifeGraphics/pugixml/docs/manual/install.html @@ -0,0 +1,430 @@ + + + +Installation + + + + + + + + + + + +
+pugixml 1.0 manual | + Overview | + Installation | + Document: + Object model · Loading · Accessing · Modifying · Saving | + XPath | + API Reference | + Table of Contents +
+PrevUpHomeNext +
+
+
+ + +
+ +

+ pugixml is distributed in source form. You can either download a source distribution + or checkout the Subversion repository. +

+
+ +

+ You can download the latest source distribution via one of the following + links: +

+
http://pugixml.googlecode.com/files/pugixml-1.0.zip
+http://pugixml.googlecode.com/files/pugixml-1.0.tar.gz
+
+

+ The distribution contains library source, documentation (the manual you're + reading now and the quick start guide) and some code examples. After downloading + the distribution, install pugixml by extracting all files from the compressed + archive. The files have different line endings depending on the archive + format - .zip archive has Windows line endings, .tar.gz archive has Unix + line endings. Otherwise the files in both archives are identical. +

+

+ If you need an older version, you can download it from the version + archive. +

+
+
+ +

+ The Subversion repository is located at http://pugixml.googlecode.com/svn/. + There is a Subversion tag "release-{version}" for each version; + also there is the "latest" tag, which always points to the latest + stable release. +

+

+ For example, to checkout the current version, you can use this command: +

+
svn checkout http://pugixml.googlecode.com/svn/tags/release-1.0 pugixml
+

+ To checkout the latest version, you can use this command: +

+
svn checkout http://pugixml.googlecode.com/svn/tags/latest pugixml
+

+ The repository contains library source, documentation, code examples and + full unit test suite. +

+

+ Use latest version tag if you want to automatically get new versions via + svn update. Use other tags if you want to switch to + new versions only explicitly (for example, using svn switch + command). Also please note that Subversion trunk contains the work-in-progress + version of the code; while this means that you can get new features and + bug fixes from trunk without waiting for a new release, this also means + that occasionally the code can be broken in some configurations. +

+
+
+
+ +

+ pugixml is distributed in source form without any pre-built binaries; you + have to build them yourself. +

+

+ The complete pugixml source consists of three files - one source file, pugixml.cpp, + and two header files, pugixml.hpp and pugiconfig.hpp. pugixml.hpp is the primary + header which you need to include in order to use pugixml classes/functions; + pugiconfig.hpp is a supplementary configuration file (see Additional configuration + options). + The rest of this guide assumes that pugixml.hpp is either in the current directory + or in one of include directories of your projects, so that #include "pugixml.hpp" + can find the header; however you can also use relative path (i.e. #include "../libs/pugixml/src/pugixml.hpp") + or include directory-relative path (i.e. #include + <xml/thirdparty/pugixml/src/pugixml.hpp>). +

+
+ +

+ The easiest way to build pugixml is to compile the source file, pugixml.cpp, + along with the existing library/executable. This process depends on the + method of building your application; for example, if you're using Microsoft + Visual Studio[1], Apple Xcode, Code::Blocks or any other IDE, just add pugixml.cpp to + one of your projects. +

+

+ If you're using Microsoft Visual Studio and the project has precompiled + headers turned on, you'll see the following error messages: +

+
pugixml.cpp(3477) : fatal error C1010: unexpected end of file while looking for precompiled header. Did you forget to add '#include "stdafx.h"' to your source?
+

+ The correct way to resolve this is to disable precompiled headers for pugixml.cpp; + you have to set "Create/Use Precompiled Header" option (Properties + dialog -> C/C++ -> Precompiled Headers -> Create/Use Precompiled + Header) to "Not Using Precompiled Headers". You'll have to do + it for all project configurations/platforms (you can select Configuration + "All Configurations" and Platform "All Platforms" before + editing the option): +

+
+ + +
+

+ vs2005_pch1_thumb next vs2005_pch2_thumb next vs2005_pch3_thumb next vs2005_pch4_thumb +

+
+
+
+ +

+ It's possible to compile pugixml as a standalone static library. This process + depends on the method of building your application; pugixml distribution + comes with project files for several popular IDEs/build systems. There + are project files for Apple XCode3, Code::Blocks, Codelite, Microsoft Visual + Studio 2005, 2008, 2010, and configuration scripts for CMake and premake4. + You're welcome to submit project files/build scripts for other software; + see Feedback. +

+

+ There are two projects for each version of Microsoft Visual Studio: one + for dynamically linked CRT, which has a name like pugixml_vs2008.vcproj, + and another one for statically linked CRT, which has a name like pugixml_vs2008_static.vcproj. + You should select the version that matches the CRT used in your application; + the default option for new projects created by Microsoft Visual Studio + is dynamically linked CRT, so unless you changed the defaults, you should + use the version with dynamic CRT (i.e. pugixml_vs2008.vcproj for Microsoft + Visual Studio 2008). +

+

+ In addition to adding pugixml project to your workspace, you'll have to + make sure that your application links with pugixml library. If you're using + Microsoft Visual Studio 2005/2008, you can add a dependency from your application + project to pugixml one. If you're using Microsoft Visual Studio 2010, you'll + have to add a reference to your application project instead. For other + IDEs/systems, consult the relevant documentation. +

+
++++ + + + + + + + + +
+

+ Microsoft Visual Studio 2005/2008 +

+
+

+ Microsoft Visual Studio 2010 +

+
+

+ vs2005_link1_thumb next vs2005_link2_thumb +

+
+

+ vs2010_link1_thumb next vs2010_link2_thumb +

+
+
+
+ +

+ It's possible to compile pugixml as a standalone shared library. The process + is usually similar to the static library approach; however, no preconfigured + projects/scripts are included into pugixml distribution, so you'll have + to do it yourself. Generally, if you're using GCC-based toolchain, the + process does not differ from building any other library as DLL (adding + -shared to compilation flags should suffice); if you're using MSVC-based + toolchain, you'll have to explicitly mark exported symbols with a declspec + attribute. You can do it by defining PUGIXML_API + macro, i.e. via pugiconfig.hpp: +

+
#ifdef _DLL
+#define PUGIXML_API __declspec(dllexport)
+#else
+#define PUGIXML_API __declspec(dllimport)
+#endif
+
+
+ + + + + +
[Caution]Caution

+ If you're using STL-related functions, you should use the shared runtime + library to ensure that a single heap is used for STL allocations in your + application and in pugixml; in MSVC, this means selecting the 'Multithreaded + DLL' or 'Multithreaded Debug DLL' to 'Runtime library' property (/MD + or /MDd linker switch). You should also make sure that your runtime library + choice is consistent between different projects. +

+
+
+ +

+ pugixml uses several defines to control the compilation process. There + are two ways to define them: either put the needed definitions to pugiconfig.hpp (it + has some examples that are commented out) or provide them via compiler + command-line. Consistency is important: the definitions should match in + all source files that include pugixml.hpp (including pugixml sources) throughout + the application. Adding defines to pugiconfig.hpp lets you guarantee this, + unless your macro definition is wrapped in preprocessor #if/#ifdef directive and this directive + is not consistent. pugiconfig.hpp will never contain anything but comments, + which means that when upgrading to a new version, you can safely leave + your modified version intact. +

+

+ PUGIXML_WCHAR_MODE define toggles + between UTF-8 style interface (the in-memory text encoding is assumed to + be UTF-8, most functions use char + as character type) and UTF-16/32 style interface (the in-memory text encoding + is assumed to be UTF-16/32, depending on wchar_t + size, most functions use wchar_t + as character type). See Unicode interface for more details. +

+

+ PUGIXML_NO_XPATH define disables XPath. + Both XPath interfaces and XPath implementation are excluded from compilation. + This option is provided in case you do not need XPath functionality and + need to save code space. +

+

+ PUGIXML_NO_STL define disables use of + STL in pugixml. The functions that operate on STL types are no longer present + (i.e. load/save via iostream) if this macro is defined. This option is + provided in case your target platform does not have a standard-compliant + STL implementation. +

+

+ PUGIXML_NO_EXCEPTIONS define disables + use of exceptions in pugixml. This option is provided in case your target + platform does not have exception handling capabilities. +

+

+ PUGIXML_API, PUGIXML_CLASS + and PUGIXML_FUNCTION defines let you + specify custom attributes (i.e. declspec or calling conventions) for pugixml + classes and non-member functions. In absence of PUGIXML_CLASS + or PUGIXML_FUNCTION definitions, + PUGIXML_API definition + is used instead. For example, to specify fixed calling convention, you + can define PUGIXML_FUNCTION + to i.e. __fastcall. Another + example is DLL import/export attributes in MSVC (see Building pugixml as + a standalone shared library). +

+
+ + + + + +
[Note]Note

+ In that example PUGIXML_API + is inconsistent between several source files; this is an exception to + the consistency rule. +

+
+
+
+ +

+ pugixml is written in standard-compliant C++ with some compiler-specific + workarounds where appropriate. pugixml is compatible with the upcoming C++0x + standard (verified using GCC 4.5). Each version is tested with a unit test + suite (with code coverage about 99%) on the following platforms: +

+
    +
  • + Microsoft Windows: +
      +
    • + Borland C++ Compiler 5.82 +
    • +
    • + Digital Mars C++ Compiler 8.51 +
    • +
    • + Intel C++ Compiler 8.0, 9.0 x86/x64, 10.0 x86/x64, 11.0 x86/x64 +
    • +
    • + Metrowerks CodeWarrior 8.0 +
    • +
    • + Microsoft Visual C++ 6.0, 7.0 (2002), 7.1 (2003), 8.0 (2005) x86/x64, + 9.0 (2008) x86/x64, 10.0 (2010) x86/x64 +
    • +
    • + MinGW (GCC) 3.4, 4.4, 4.5, 4.6 x64 +
    • +
    +
  • +
  • + Linux (GCC 4.4.3 x86/x64) +
  • +
  • + FreeBSD (GCC 4.2.1 x86/x64) +
  • +
  • + Apple MacOSX (GCC 4.0.1 x86/x64/PowerPC) +
  • +
  • + Microsoft Xbox 360 +
  • +
  • + Nintendo Wii (Metrowerks CodeWarrior 4.1) +
  • +
  • + Sony Playstation Portable (GCC 3.4.2) +
  • +
  • + Sony Playstation 3 (GCC 4.1.1, SNC 310.1) +
  • +
+
+
+

+

[1] All trademarks used are properties of their respective + owners.

+
+
+ + + +
+
+ + + +
+pugixml 1.0 manual | + Overview | + Installation | + Document: + Object model · Loading · Accessing · Modifying · Saving | + XPath | + API Reference | + Table of Contents +
+PrevUpHomeNext +
+ + diff --git a/LifeGraphics/pugixml/docs/manual/loading.html b/LifeGraphics/pugixml/docs/manual/loading.html new file mode 100644 index 0000000..5b5576b --- /dev/null +++ b/LifeGraphics/pugixml/docs/manual/loading.html @@ -0,0 +1,853 @@ + + + +Loading document + + + + + + + + + + + +
+pugixml 1.0 manual | + Overview | + Installation | + Document: + Object model · Loading · Accessing · Modifying · Saving | + XPath | + API Reference | + Table of Contents +
+PrevUpHomeNext +
+
+
+ + +

+ pugixml provides several functions for loading XML data from various places + - files, C++ iostreams, memory buffers. All functions use an extremely fast + non-validating parser. This parser is not fully W3C conformant - it can load + any valid XML document, but does not perform some well-formedness checks. While + considerable effort is made to reject invalid XML documents, some validation + is not performed for performance reasons. Also some XML transformations (i.e. + EOL handling or attribute value normalization) can impact parsing speed and + thus can be disabled. However for vast majority of XML documents there is no + performance difference between different parsing options. Parsing options also + control whether certain XML nodes are parsed; see Parsing options for + more information. +

+

+ XML data is always converted to internal character format (see Unicode interface) + before parsing. pugixml supports all popular Unicode encodings (UTF-8, UTF-16 + (big and little endian), UTF-32 (big and little endian); UCS-2 is naturally + supported since it's a strict subset of UTF-16) and handles all encoding conversions + automatically. Unless explicit encoding is specified, loading functions perform + automatic encoding detection based on first few characters of XML data, so + in almost all cases you do not have to specify document encoding. Encoding + conversion is described in more detail in Encodings. +

+
+ +

+ The most common source of XML data is files; pugixml provides dedicated functions + for loading an XML document from file: +

+
xml_parse_result xml_document::load_file(const char* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
+xml_parse_result xml_document::load_file(const wchar_t* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
+
+

+ These functions accept the file path as its first argument, and also two + optional arguments, which specify parsing options (see Parsing options) + and input data encoding (see Encodings). The path has the target + operating system format, so it can be a relative or absolute one, it should + have the delimiters of the target system, it should have the exact case if + the target file system is case-sensitive, etc. +

+

+ File path is passed to the system file opening function as is in case of + the first function (which accepts const + char* path); the second function either uses + a special file opening function if it is provided by the runtime library + or converts the path to UTF-8 and uses the system file opening function. +

+

+ load_file destroys the existing + document tree and then tries to load the new tree from the specified file. + The result of the operation is returned in an xml_parse_result + object; this object contains the operation status and the related information + (i.e. last successfully parsed position in the input file, if parsing fails). + See Handling parsing errors for error handling details. +

+

+ This is an example of loading XML document from file (samples/load_file.cpp): +

+

+ +

+
pugi::xml_document doc;
+
+pugi::xml_parse_result result = doc.load_file("tree.xml");
+
+std::cout << "Load result: " << result.description() << ", mesh name: " << doc.child("mesh").attribute("name").value() << std::endl;
+
+

+

+
+
+ +

+ Sometimes XML data should be loaded from some other source than a file, i.e. + HTTP URL; also you may want to load XML data from file using non-standard + functions, i.e. to use your virtual file system facilities or to load XML + from gzip-compressed files. All these scenarios require loading document + from memory. First you should prepare a contiguous memory block with all + XML data; then you have to invoke one of buffer loading functions. These + functions will handle the necessary encoding conversions, if any, and then + will parse the data into the corresponding XML tree. There are several buffer + loading functions, which differ in the behavior and thus in performance/memory + usage: +

+
xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
+xml_parse_result xml_document::load_buffer_inplace(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
+xml_parse_result xml_document::load_buffer_inplace_own(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
+
+

+ All functions accept the buffer which is represented by a pointer to XML + data, contents, and data + size in bytes. Also there are two optional arguments, which specify parsing + options (see Parsing options) and input data encoding (see Encodings). + The buffer does not have to be zero-terminated. +

+

+ load_buffer function works + with immutable buffer - it does not ever modify the buffer. Because of this + restriction it has to create a private buffer and copy XML data to it before + parsing (applying encoding conversions if necessary). This copy operation + carries a performance penalty, so inplace functions are provided - load_buffer_inplace and load_buffer_inplace_own + store the document data in the buffer, modifying it in the process. In order + for the document to stay valid, you have to make sure that the buffer's lifetime + exceeds that of the tree if you're using inplace functions. In addition to + that, load_buffer_inplace + does not assume ownership of the buffer, so you'll have to destroy it yourself; + load_buffer_inplace_own assumes + ownership of the buffer and destroys it once it is not needed. This means + that if you're using load_buffer_inplace_own, + you have to allocate memory with pugixml allocation function (you can get + it via get_memory_allocation_function). +

+

+ The best way from the performance/memory point of view is to load document + using load_buffer_inplace_own; + this function has maximum control of the buffer with XML data so it is able + to avoid redundant copies and reduce peak memory usage while parsing. This + is the recommended function if you have to load the document from memory + and performance is critical. +

+

+ There is also a simple helper function for cases when you want to load the + XML document from null-terminated character string: +

+
xml_parse_result xml_document::load(const char_t* contents, unsigned int options = parse_default);
+
+

+ It is equivalent to calling load_buffer + with size being either strlen(contents) + or wcslen(contents) * sizeof(wchar_t), + depending on the character type. This function assumes native encoding for + input data, so it does not do any encoding conversion. In general, this function + is fine for loading small documents from string literals, but has more overhead + and less functionality than the buffer loading functions. +

+

+ This is an example of loading XML document from memory using different functions + (samples/load_memory.cpp): +

+

+ +

+
const char source[] = "<mesh name='sphere'><bounds>0 0 1 1</bounds></mesh>";
+size_t size = sizeof(source);
+
+

+

+

+ +

+
// You can use load_buffer to load document from immutable memory block:
+pugi::xml_parse_result result = doc.load_buffer(source, size);
+
+

+

+

+ +

+
// You can use load_buffer_inplace to load document from mutable memory block; the block's lifetime must exceed that of document
+char* buffer = new char[size];
+memcpy(buffer, source, size);
+
+// The block can be allocated by any method; the block is modified during parsing
+pugi::xml_parse_result result = doc.load_buffer_inplace(buffer, size);
+
+// You have to destroy the block yourself after the document is no longer used
+delete[] buffer;
+
+

+

+

+ +

+
// You can use load_buffer_inplace_own to load document from mutable memory block and to pass the ownership of this block
+// The block has to be allocated via pugixml allocation function - using i.e. operator new here is incorrect
+char* buffer = static_cast<char*>(pugi::get_memory_allocation_function()(size));
+memcpy(buffer, source, size);
+
+// The block will be deleted by the document
+pugi::xml_parse_result result = doc.load_buffer_inplace_own(buffer, size);
+
+

+

+

+ +

+
// You can use load to load document from null-terminated strings, for example literals:
+pugi::xml_parse_result result = doc.load("<mesh name='sphere'><bounds>0 0 1 1</bounds></mesh>");
+
+

+

+
+
+ +

+ To enhance interoperability, pugixml provides functions for loading document + from any object which implements C++ std::istream + interface. This allows you to load documents from any standard C++ stream + (i.e. file stream) or any third-party compliant implementation (i.e. Boost + Iostreams). There are two functions, one works with narrow character streams, + another handles wide character ones: +

+
xml_parse_result xml_document::load(std::istream& stream, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
+xml_parse_result xml_document::load(std::wistream& stream, unsigned int options = parse_default);
+
+

+ load with std::istream + argument loads the document from stream from the current read position to + the end, treating the stream contents as a byte stream of the specified encoding + (with encoding autodetection as necessary). Thus calling xml_document::load + on an opened std::ifstream object is equivalent to calling + xml_document::load_file. +

+

+ load with std::wstream + argument treats the stream contents as a wide character stream (encoding + is always encoding_wchar). Because + of this, using load with + wide character streams requires careful (usually platform-specific) stream + setup (i.e. using the imbue + function). Generally use of wide streams is discouraged, however it provides + you the ability to load documents from non-Unicode encodings, i.e. you can + load Shift-JIS encoded data if you set the correct locale. +

+

+ This is a simple example of loading XML document from file using streams + (samples/load_stream.cpp); read + the sample code for more complex examples involving wide streams and locales: +

+

+ +

+
std::ifstream stream("weekly-utf-8.xml");
+pugi::xml_parse_result result = doc.load(stream);
+
+

+

+

+ Stream loading requires working seek/tell functions and therefore may fail + when used with some stream implementations like gzstream. +

+
+
+ +

+ All document loading functions return the parsing result via xml_parse_result object. It contains parsing + status, the offset of last successfully parsed character from the beginning + of the source stream, and the encoding of the source stream: +

+
struct xml_parse_result
+{
+    xml_parse_status status;
+    ptrdiff_t offset;
+    xml_encoding encoding;
+
+    operator bool() const;
+    const char* description() const;
+};
+
+

+ Parsing status is represented as the xml_parse_status + enumeration and can be one of the following: +

+
    +
  • + status_ok means that no error was encountered + during parsing; the source stream represents the valid XML document which + was fully parsed and converted to a tree.

    + +
  • +
  • + status_file_not_found is only + returned by load_file + function and means that file could not be opened. +
  • +
  • + status_io_error is returned by load_file function and by load functions with std::istream/std::wstream arguments; it means that some + I/O error has occurred during reading the file/stream. +
  • +
  • + status_out_of_memory means that + there was not enough memory during some allocation; any allocation failure + during parsing results in this error. +
  • +
  • + status_internal_error means that + something went horribly wrong; currently this error does not occur

    + +
  • +
  • + status_unrecognized_tag means + that parsing stopped due to a tag with either an empty name or a name + which starts with incorrect character, such as #. +
  • +
  • + status_bad_pi means that parsing stopped + due to incorrect document declaration/processing instruction +
  • +
  • + status_bad_comment, status_bad_cdata, + status_bad_doctype and status_bad_pcdata + mean that parsing stopped due to the invalid construct of the respective + type +
  • +
  • + status_bad_start_element means + that parsing stopped because starting tag either had no closing > symbol or contained some incorrect + symbol +
  • +
  • + status_bad_attribute means that + parsing stopped because there was an incorrect attribute, such as an + attribute without value or with value that is not quoted (note that + <node + attr=1> is + incorrect in XML) +
  • +
  • + status_bad_end_element means + that parsing stopped because ending tag had incorrect syntax (i.e. extra + non-whitespace symbols between tag name and >) +
  • +
  • + status_end_element_mismatch + means that parsing stopped because the closing tag did not match the + opening one (i.e. <node></nedo>) or because some tag was not closed + at all +
  • +
+

+ description() + member function can be used to convert parsing status to a string; the returned + message is always in English, so you'll have to write your own function if + you need a localized string. However please note that the exact messages + returned by description() + function may change from version to version, so any complex status handling + should be based on status + value. +

+

+ If parsing failed because the source data was not a valid XML, the resulting + tree is not destroyed - despite the fact that load function returns error, + you can use the part of the tree that was successfully parsed. Obviously, + the last element may have an unexpected name/value; for example, if the attribute + value does not end with the necessary quotation mark, like in <node + attr="value>some data</node> example, the value of + attribute attr will contain + the string value>some data</node>. +

+

+ In addition to the status code, parsing result has an offset + member, which contains the offset of last successfully parsed character if + parsing failed because of an error in source data; otherwise offset is 0. For parsing efficiency reasons, + pugixml does not track the current line during parsing; this offset is in + units of pugi::char_t (bytes for character + mode, wide characters for wide character mode). Many text editors support + 'Go To Position' feature - you can use it to locate the exact error position. + Alternatively, if you're loading the document from memory, you can display + the error chunk along with the error description (see the example code below). +

+
+ + + + + +
[Caution]Caution

+ Offset is calculated in the XML buffer in native encoding; if encoding + conversion is performed during parsing, offset can not be used to reliably + track the error position. +

+

+ Parsing result also has an encoding + member, which can be used to check that the source data encoding was correctly + guessed. It is equal to the exact encoding used during parsing (i.e. with + the exact endianness); see Encodings for more information. +

+

+ Parsing result object can be implicitly converted to bool; + if you do not want to handle parsing errors thoroughly, you can just check + the return value of load functions as if it was a bool: + if (doc.load_file("file.xml")) { ... + } else { ... }. +

+

+ This is an example of handling loading errors (samples/load_error_handling.cpp): +

+

+ +

+
pugi::xml_document doc;
+pugi::xml_parse_result result = doc.load(source);
+
+if (result)
+    std::cout << "XML [" << source << "] parsed without errors, attr value: [" << doc.child("node").attribute("attr").value() << "]\n\n";
+else
+{
+    std::cout << "XML [" << source << "] parsed with errors, attr value: [" << doc.child("node").attribute("attr").value() << "]\n";
+    std::cout << "Error description: " << result.description() << "\n";
+    std::cout << "Error offset: " << result.offset << " (error at [..." << (source + result.offset) << "]\n\n";
+}
+
+

+

+
+
+ +

+ All document loading functions accept the optional parameter options. This is a bitmask that customizes + the parsing process: you can select the node types that are parsed and various + transformations that are performed with the XML text. Disabling certain transformations + can improve parsing performance for some documents; however, the code for + all transformations is very well optimized, and thus the majority of documents + won't get any performance benefit. As a rule of thumb, only modify parsing + flags if you want to get some nodes in the document that are excluded by + default (i.e. declaration or comment nodes). +

+
+ + + + + +
[Note]Note

+ You should use the usual bitwise arithmetics to manipulate the bitmask: + to enable a flag, use mask | flag; + to disable a flag, use mask & ~flag. +

+

+ These flags control the resulting tree contents: +

+
    +
  • + parse_declaration determines if XML + document declaration (node with type node_declaration) + is to be put in DOM tree. If this flag is off, it is not put in the tree, + but is still parsed and checked for correctness. This flag is off by default.

    + +
  • +
  • + parse_doctype determines if XML document + type declaration (node with type node_doctype) + is to be put in DOM tree. If this flag is off, it is not put in the tree, + but is still parsed and checked for correctness. This flag is off by default.

    + +
  • +
  • + parse_pi determines if processing instructions + (nodes with type node_pi) are to be put + in DOM tree. If this flag is off, they are not put in the tree, but are + still parsed and checked for correctness. Note that <?xml ...?> + (document declaration) is not considered to be a PI. This flag is off by default.

    + +
  • +
  • + parse_comments determines if comments + (nodes with type node_comment) are + to be put in DOM tree. If this flag is off, they are not put in the tree, + but are still parsed and checked for correctness. This flag is off by default.

    + +
  • +
  • + parse_cdata determines if CDATA sections + (nodes with type node_cdata) are to + be put in DOM tree. If this flag is off, they are not put in the tree, + but are still parsed and checked for correctness. This flag is on by default.

    + +
  • +
  • + parse_ws_pcdata determines if PCDATA + nodes (nodes with type node_pcdata) + that consist only of whitespace characters are to be put in DOM tree. + Often whitespace-only data is not significant for the application, and + the cost of allocating and storing such nodes (both memory and speed-wise) + can be significant. For example, after parsing XML string <node> <a/> </node>, <node> + element will have three children when parse_ws_pcdata + is set (child with type node_pcdata + and value " ", + child with type node_element and + name "a", and another + child with type node_pcdata and value + " "), and only + one child when parse_ws_pcdata + is not set. This flag is off by default. +
  • +
+

+ These flags control the transformation of tree element contents: +

+
    +
  • + parse_escapes determines if character + and entity references are to be expanded during the parsing process. + Character references have the form &#...; or + &#x...; (... is Unicode numeric + representation of character in either decimal (&#...;) + or hexadecimal (&#x...;) form), entity references + are &lt;, &gt;, &amp;, + &apos; and &quot; (note + that as pugixml does not handle DTD, the only allowed entities are predefined + ones). If character/entity reference can not be expanded, it is left + as is, so you can do additional processing later. Reference expansion + is performed on attribute values and PCDATA content. This flag is on by default.

    + +
  • +
  • + parse_eol determines if EOL handling (that + is, replacing sequences 0x0d 0x0a by a single 0x0a + character, and replacing all standalone 0x0d + characters by 0x0a) is to + be performed on input data (that is, comments contents, PCDATA/CDATA + contents and attribute values). This flag is on + by default.

    + +
  • +
  • + parse_wconv_attribute determines + if attribute value normalization should be performed for all attributes. + This means, that whitespace characters (new line, tab and space) are + replaced with space (' '). + New line characters are always treated as if parse_eol + is set, i.e. \r\n + is converted to a single space. This flag is on + by default.

    + +
  • +
  • + parse_wnorm_attribute determines + if extended attribute value normalization should be performed for all + attributes. This means, that after attribute values are normalized as + if parse_wconv_attribute + was set, leading and trailing space characters are removed, and all sequences + of space characters are replaced by a single space character. The value + of parse_wconv_attribute + has no effect if this flag is on. This flag is off + by default. +
  • +
+
+ + + + + +
[Note]Note

+ parse_wconv_attribute option + performs transformations that are required by W3C specification for attributes + that are declared as CDATA; parse_wnorm_attribute + performs transformations required for NMTOKENS attributes. + In the absence of document type declaration all attributes should behave + as if they are declared as CDATA, thus parse_wconv_attribute + is the default option. +

+

+ Additionally there are three predefined option masks: +

+
    +
  • + parse_minimal has all options turned + off. This option mask means that pugixml does not add declaration nodes, + document type declaration nodes, PI nodes, CDATA sections and comments + to the resulting tree and does not perform any conversion for input data, + so theoretically it is the fastest mode. However, as mentioned above, + in practice parse_default is usually + equally fast.

    + +
  • +
  • + parse_default is the default set of flags, + i.e. it has all options set to their default values. It includes parsing + CDATA sections (comments/PIs are not parsed), performing character and + entity reference expansion, replacing whitespace characters with spaces + in attribute values and performing EOL handling. Note, that PCDATA sections + consisting only of whitespace characters are not parsed (by default) + for performance reasons.

    + +
  • +
  • + parse_full is the set of flags which adds + nodes of all types to the resulting tree and performs default conversions + for input data. It includes parsing CDATA sections, comments, PI nodes, + document declaration node and document type declaration node, performing + character and entity reference expansion, replacing whitespace characters + with spaces in attribute values and performing EOL handling. Note, that + PCDATA sections consisting only of whitespace characters are not parsed + in this mode. +
  • +
+

+ This is an example of using different parsing options (samples/load_options.cpp): +

+

+ +

+
const char* source = "<!--comment--><node>&lt;</node>";
+
+// Parsing with default options; note that comment node is not added to the tree, and entity reference &lt; is expanded
+doc.load(source);
+std::cout << "First node value: [" << doc.first_child().value() << "], node child value: [" << doc.child_value("node") << "]\n";
+
+// Parsing with additional parse_comments option; comment node is now added to the tree
+doc.load(source, pugi::parse_default | pugi::parse_comments);
+std::cout << "First node value: [" << doc.first_child().value() << "], node child value: [" << doc.child_value("node") << "]\n";
+
+// Parsing with additional parse_comments option and without the (default) parse_escapes option; &lt; is not expanded
+doc.load(source, (pugi::parse_default | pugi::parse_comments) & ~pugi::parse_escapes);
+std::cout << "First node value: [" << doc.first_child().value() << "], node child value: [" << doc.child_value("node") << "]\n";
+
+// Parsing with minimal option mask; comment node is not added to the tree, and &lt; is not expanded
+doc.load(source, pugi::parse_minimal);
+std::cout << "First node value: [" << doc.first_child().value() << "], node child value: [" << doc.child_value("node") << "]\n";
+
+

+

+
+
+ +

+ pugixml supports all popular Unicode encodings (UTF-8, UTF-16 (big and little + endian), UTF-32 (big and little endian); UCS-2 is naturally supported since + it's a strict subset of UTF-16) and handles all encoding conversions. Most + loading functions accept the optional parameter encoding. + This is a value of enumeration type xml_encoding, + that can have the following values: +

+
    +
  • + encoding_auto means that pugixml will + try to guess the encoding based on source XML data. The algorithm is + a modified version of the one presented in Appendix F.1 of XML recommendation; + it tries to match the first few bytes of input data with the following + patterns in strict order:

    +
      +
    • + If first four bytes match UTF-32 BOM (Byte Order Mark), encoding + is assumed to be UTF-32 with the endianness equal to that of BOM; +
    • +
    • + If first two bytes match UTF-16 BOM, encoding is assumed to be + UTF-16 with the endianness equal to that of BOM; +
    • +
    • + If first three bytes match UTF-8 BOM, encoding is assumed to be + UTF-8; +
    • +
    • + If first four bytes match UTF-32 representation of <, + encoding is assumed to be UTF-32 with the corresponding endianness; +
    • +
    • + If first four bytes match UTF-16 representation of <?, + encoding is assumed to be UTF-16 with the corresponding endianness; +
    • +
    • + If first two bytes match UTF-16 representation of <, + encoding is assumed to be UTF-16 with the corresponding endianness + (this guess may yield incorrect result, but it's better than UTF-8); +
    • +
    • + Otherwise encoding is assumed to be UTF-8.

      + +
    • +
    +
  • +
  • + encoding_utf8 corresponds to UTF-8 encoding + as defined in the Unicode standard; UTF-8 sequences with length equal + to 5 or 6 are not standard and are rejected. +
  • +
  • + encoding_utf16_le corresponds to + little-endian UTF-16 encoding as defined in the Unicode standard; surrogate + pairs are supported. +
  • +
  • + encoding_utf16_be corresponds to + big-endian UTF-16 encoding as defined in the Unicode standard; surrogate + pairs are supported. +
  • +
  • + encoding_utf16 corresponds to UTF-16 + encoding as defined in the Unicode standard; the endianness is assumed + to be that of the target platform. +
  • +
  • + encoding_utf32_le corresponds to + little-endian UTF-32 encoding as defined in the Unicode standard. +
  • +
  • + encoding_utf32_be corresponds to + big-endian UTF-32 encoding as defined in the Unicode standard. +
  • +
  • + encoding_utf32 corresponds to UTF-32 + encoding as defined in the Unicode standard; the endianness is assumed + to be that of the target platform. +
  • +
  • + encoding_wchar corresponds to the encoding + of wchar_t type; it has + the same meaning as either encoding_utf16 + or encoding_utf32, depending + on wchar_t size. +
  • +
+

+ The algorithm used for encoding_auto + correctly detects any supported Unicode encoding for all well-formed XML + documents (since they start with document declaration) and for all other + XML documents that start with <; if your XML document + does not start with < and has encoding that is different + from UTF-8, use the specific encoding. +

+
+ + + + + +
[Note]Note

+ The current behavior for Unicode conversion is to skip all invalid UTF + sequences during conversion. This behavior should not be relied upon; moreover, + in case no encoding conversion is performed, the invalid sequences are + not removed, so you'll get them as is in node/attribute contents. +

+
+
+ +

+ pugixml is not fully W3C conformant - it can load any valid XML document, + but does not perform some well-formedness checks. While considerable effort + is made to reject invalid XML documents, some validation is not performed + because of performance reasons. +

+

+ There is only one non-conformant behavior when dealing with valid XML documents: + pugixml does not use information supplied in document type declaration for + parsing. This means that entities declared in DOCTYPE are not expanded, and + all attribute/PCDATA values are always processed in a uniform way that depends + only on parsing options. +

+

+ As for rejecting invalid XML documents, there are a number of incompatibilities + with W3C specification, including: +

+
    +
  • + Multiple attributes of the same node can have equal names. +
  • +
  • + All non-ASCII characters are treated in the same way as symbols of English + alphabet, so some invalid tag names are not rejected. +
  • +
  • + Attribute values which contain < are not rejected. +
  • +
  • + Invalid entity/character references are not rejected and are instead + left as is. +
  • +
  • + Comment values can contain --. +
  • +
  • + XML data is not required to begin with document declaration; additionally, + document declaration can appear after comments and other nodes. +
  • +
  • + Invalid document type declarations are silently ignored in some cases. +
  • +
+
+
+ + + +
+
+ + + +
+pugixml 1.0 manual | + Overview | + Installation | + Document: + Object model · Loading · Accessing · Modifying · Saving | + XPath | + API Reference | + Table of Contents +
+PrevUpHomeNext +
+ + diff --git a/LifeGraphics/pugixml/docs/manual/modify.html b/LifeGraphics/pugixml/docs/manual/modify.html new file mode 100644 index 0000000..3db02e1 --- /dev/null +++ b/LifeGraphics/pugixml/docs/manual/modify.html @@ -0,0 +1,553 @@ + + + +Modifying document data + + + + + + + + + + + +
+pugixml 1.0 manual | + Overview | + Installation | + Document: + Object model · Loading · Accessing · Modifying · Saving | + XPath | + API Reference | + Table of Contents +
+PrevUpHomeNext +
+
+
+ + +

+ The document in pugixml is fully mutable: you can completely change the document + structure and modify the data of nodes/attributes. This section provides documentation + for the relevant functions. All functions take care of memory management and + structural integrity themselves, so they always result in structurally valid + tree - however, it is possible to create an invalid XML tree (for example, + by adding two attributes with the same name or by setting attribute/node name + to empty/invalid string). Tree modification is optimized for performance and + for memory consumption, so if you have enough memory you can create documents + from scratch with pugixml and later save them to file/stream instead of relying + on error-prone manual text writing and without too much overhead. +

+

+ All member functions that change node/attribute data or structure are non-constant + and thus can not be called on constant handles. However, you can easily convert + constant handle to non-constant one by simple assignment: void + foo(const pugi::xml_node& n) + { pugi::xml_node nc = n; }, so const-correctness + here mainly provides additional documentation. +

+
+ +

+ As discussed before, nodes can have name and value, both of which are strings. + Depending on node type, name or value may be absent. node_document + nodes do not have a name or value, node_element + and node_declaration nodes always + have a name but never have a value, node_pcdata, + node_cdata, node_comment + and node_doctype nodes never have a name + but always have a value (it may be empty though), node_pi + nodes always have a name and a value (again, value may be empty). In order + to set node's name or value, you can use the following functions: +

+
bool xml_node::set_name(const char_t* rhs);
+bool xml_node::set_value(const char_t* rhs);
+
+

+ Both functions try to set the name/value to the specified string, and return + the operation result. The operation fails if the node can not have name or + value (for instance, when trying to call set_name + on a node_pcdata node), if the node handle + is null, or if there is insufficient memory to handle the request. The provided + string is copied into document managed memory and can be destroyed after + the function returns (for example, you can safely pass stack-allocated buffers + to these functions). The name/value content is not verified, so take care + to use only valid XML names, or the document may become malformed. +

+

+ There is no equivalent of child_value + function for modifying text children of the node. +

+

+ This is an example of setting node name and value (samples/modify_base.cpp): +

+

+ +

+
pugi::xml_node node = doc.child("node");
+
+// change node name
+std::cout << node.set_name("notnode");
+std::cout << ", new node name: " << node.name() << std::endl;
+
+// change comment text
+std::cout << doc.last_child().set_value("useless comment");
+std::cout << ", new comment text: " << doc.last_child().value() << std::endl;
+
+// we can't change value of the element or name of the comment
+std::cout << node.set_value("1") << ", " << doc.last_child().set_name("2") << std::endl;
+
+

+

+
+
+ +

+ All attributes have name and value, both of which are strings (value may + be empty). You can set them with the following functions: +

+
bool xml_attribute::set_name(const char_t* rhs);
+bool xml_attribute::set_value(const char_t* rhs);
+
+

+ Both functions try to set the name/value to the specified string, and return + the operation result. The operation fails if the attribute handle is null, + or if there is insufficient memory to handle the request. The provided string + is copied into document managed memory and can be destroyed after the function + returns (for example, you can safely pass stack-allocated buffers to these + functions). The name/value content is not verified, so take care to use only + valid XML names, or the document may become malformed. +

+

+ In addition to string functions, several functions are provided for handling + attributes with numbers and booleans as values: +

+
bool xml_attribute::set_value(int rhs);
+bool xml_attribute::set_value(unsigned int rhs);
+bool xml_attribute::set_value(double rhs);
+bool xml_attribute::set_value(bool rhs);
+
+

+ The above functions convert the argument to string and then call the base + set_value function. Integers + are converted to a decimal form, floating-point numbers are converted to + either decimal or scientific form, depending on the number magnitude, boolean + values are converted to either "true" + or "false". +

+
+ + + + + +
[Caution]Caution

+ Number conversion functions depend on current C locale as set with setlocale, so may generate unexpected + results if the locale is different from "C". +

+
+ + + + + +
[Note]Note

+ There are no portable 64-bit types in C++, so there is no corresponding + set_value function. If + your platform has a 64-bit integer, you can easily write such a function + yourself. +

+

+ For convenience, all set_value + functions have the corresponding assignment operators: +

+
xml_attribute& xml_attribute::operator=(const char_t* rhs);
+xml_attribute& xml_attribute::operator=(int rhs);
+xml_attribute& xml_attribute::operator=(unsigned int rhs);
+xml_attribute& xml_attribute::operator=(double rhs);
+xml_attribute& xml_attribute::operator=(bool rhs);
+
+

+ These operators simply call the right set_value + function and return the attribute they're called on; the return value of + set_value is ignored, so + errors are ignored. +

+

+ This is an example of setting attribute name and value (samples/modify_base.cpp): +

+

+ +

+
pugi::xml_attribute attr = node.attribute("id");
+
+// change attribute name/value
+std::cout << attr.set_name("key") << ", " << attr.set_value("345");
+std::cout << ", new attribute: " << attr.name() << "=" << attr.value() << std::endl;
+
+// we can use numbers or booleans
+attr.set_value(1.234);
+std::cout << "new attribute value: " << attr.value() << std::endl;
+
+// we can also use assignment operators for more concise code
+attr = true;
+std::cout << "final attribute value: " << attr.value() << std::endl;
+
+

+

+
+
+ +

+ Nodes and attributes do not exist without a document tree, so you can't create + them without adding them to some document. A node or attribute can be created + at the end of node/attribute list or before/after some other node: +

+
xml_attribute xml_node::append_attribute(const char_t* name);
+xml_attribute xml_node::prepend_attribute(const char_t* name);
+xml_attribute xml_node::insert_attribute_after(const char_t* name, const xml_attribute& attr);
+xml_attribute xml_node::insert_attribute_before(const char_t* name, const xml_attribute& attr);
+
+xml_node xml_node::append_child(xml_node_type type = node_element);
+xml_node xml_node::prepend_child(xml_node_type type = node_element);
+xml_node xml_node::insert_child_after(xml_node_type type, const xml_node& node);
+xml_node xml_node::insert_child_before(xml_node_type type, const xml_node& node);
+
+xml_node xml_node::append_child(const char_t* name);
+xml_node xml_node::prepend_child(const char_t* name);
+xml_node xml_node::insert_child_after(const char_t* name, const xml_node& node);
+xml_node xml_node::insert_child_before(const char_t* name, const xml_node& node);
+
+

+ append_attribute and append_child create a new node/attribute + at the end of the corresponding list of the node the method is called on; + prepend_attribute and prepend_child create a new node/attribute + at the beginning of the list; insert_attribute_after, + insert_attribute_before, + insert_child_after and insert_attribute_before add the node/attribute + before or after the specified node/attribute. +

+

+ Attribute functions create an attribute with the specified name; you can + specify the empty name and change the name later if you want to. Node functions + with the type argument create + the node with the specified type; since node type can't be changed, you have + to know the desired type beforehand. Also note that not all types can be + added as children; see below for clarification. Node functions with the + name argument create the + element node (node_element) with the + specified name. +

+

+ All functions return the handle to the created object on success, and null + handle on failure. There are several reasons for failure: +

+
    +
  • + Adding fails if the target node is null; +
  • +
  • + Only node_element nodes can contain + attributes, so attribute adding fails if node is not an element; +
  • +
  • + Only node_document and node_element + nodes can contain children, so child node adding fails if the target + node is not an element or a document; +
  • +
  • + node_document and node_null + nodes can not be inserted as children, so passing node_document + or node_null value as type results in operation failure; +
  • +
  • + node_declaration nodes can only + be added as children of the document node; attempt to insert declaration + node as a child of an element node fails; +
  • +
  • + Adding node/attribute results in memory allocation, which may fail; +
  • +
  • + Insertion functions fail if the specified node or attribute is null or + is not in the target node's children/attribute list. +
  • +
+

+ Even if the operation fails, the document remains in consistent state, but + the requested node/attribute is not added. +

+
+ + + + + +
[Caution]Caution

+ attribute() and child() functions do not add attributes or nodes to the + tree, so code like node.attribute("id") = 123; will not do anything if node does not have an attribute with + name "id". Make sure + you're operating with existing attributes/nodes by adding them if necessary. +

+

+ This is an example of adding new attributes/nodes to the document (samples/modify_add.cpp): +

+

+ +

+
// add node with some name
+pugi::xml_node node = doc.append_child("node");
+
+// add description node with text child
+pugi::xml_node descr = node.append_child("description");
+descr.append_child(pugi::node_pcdata).set_value("Simple node");
+
+// add param node before the description
+pugi::xml_node param = node.insert_child_before("param", descr);
+
+// add attributes to param node
+param.append_attribute("name") = "version";
+param.append_attribute("value") = 1.1;
+param.insert_attribute_after("type", param.attribute("name")) = "float";
+
+

+

+
+
+ +

+ If you do not want your document to contain some node or attribute, you can + remove it with one of the following functions: +

+
bool xml_node::remove_attribute(const xml_attribute& a);
+bool xml_node::remove_child(const xml_node& n);
+
+

+ remove_attribute removes + the attribute from the attribute list of the node, and returns the operation + result. remove_child removes + the child node with the entire subtree (including all descendant nodes and + attributes) from the document, and returns the operation result. Removing + fails if one of the following is true: +

+
    +
  • + The node the function is called on is null; +
  • +
  • + The attribute/node to be removed is null; +
  • +
  • + The attribute/node to be removed is not in the node's attribute/child + list. +
  • +
+

+ Removing the attribute or node invalidates all handles to the same underlying + object, and also invalidates all iterators pointing to the same object. Removing + node also invalidates all past-the-end iterators to its attribute or child + node list. Be careful to ensure that all such handles and iterators either + do not exist or are not used after the attribute/node is removed. +

+

+ If you want to remove the attribute or child node by its name, two additional + helper functions are available: +

+
bool xml_node::remove_attribute(const char_t* name);
+bool xml_node::remove_child(const char_t* name);
+
+

+ These functions look for the first attribute or child with the specified + name, and then remove it, returning the result. If there is no attribute + or child with such name, the function returns false; + if there are two nodes with the given name, only the first node is deleted. + If you want to delete all nodes with the specified name, you can use code + like this: while (node.remove_child("tool")) ;. +

+

+ This is an example of removing attributes/nodes from the document (samples/modify_remove.cpp): +

+

+ +

+
// remove description node with the whole subtree
+pugi::xml_node node = doc.child("node");
+node.remove_child("description");
+
+// remove id attribute
+pugi::xml_node param = node.child("param");
+param.remove_attribute("value");
+
+// we can also remove nodes/attributes by handles
+pugi::xml_attribute id = param.attribute("name");
+param.remove_attribute(id);
+
+

+

+
+
+ +

+ With the help of previously described functions, it is possible to create + trees with any contents and structure, including cloning the existing data. + However since this is an often needed operation, pugixml provides built-in + node/attribute cloning facilities. Since nodes and attributes do not exist + without a document tree, you can't create a standalone copy - you have to + immediately insert it somewhere in the tree. For this, you can use one of + the following functions: +

+
xml_attribute xml_node::append_copy(const xml_attribute& proto);
+xml_attribute xml_node::prepend_copy(const xml_attribute& proto);
+xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr);
+xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr);
+
+xml_node xml_node::append_copy(const xml_node& proto);
+xml_node xml_node::prepend_copy(const xml_node& proto);
+xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node);
+xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node);
+
+

+ These functions mirror the structure of append_child, + prepend_child, insert_child_before and related functions + - they take the handle to the prototype object, which is to be cloned, insert + a new attribute/node at the appropriate place, and then copy the attribute + data or the whole node subtree to the new object. The functions return the + handle to the resulting duplicate object, or null handle on failure. +

+

+ The attribute is copied along with the name and value; the node is copied + along with its type, name and value; additionally attribute list and all + children are recursively cloned, resulting in the deep subtree clone. The + prototype object can be a part of the same document, or a part of any other + document. +

+

+ The failure conditions resemble those of append_child, + insert_child_before and related + functions, consult their documentation + for more information. There are additional caveats specific to cloning + functions: +

+
    +
  • + Cloning null handles results in operation failure; +
  • +
  • + Node cloning starts with insertion of the node of the same type as that + of the prototype; for this reason, cloning functions can not be directly + used to clone entire documents, since node_document + is not a valid insertion type. The example below provides a workaround. +
  • +
  • + It is possible to copy a subtree as a child of some node inside this + subtree, i.e. node.append_copy(node.parent().parent());. + This is a valid operation, and it results in a clone of the subtree in + the state before cloning started, i.e. no infinite recursion takes place. +
  • +
+

+ This is an example with one possible implementation of include tags in XML + (samples/include.cpp). It illustrates + node cloning and usage of other document modification functions: +

+

+ +

+
bool load_preprocess(pugi::xml_document& doc, const char* path);
+
+bool preprocess(pugi::xml_node node)
+{
+    for (pugi::xml_node child = node.first_child(); child; )
+    {
+        if (child.type() == pugi::node_pi && strcmp(child.name(), "include") == 0)
+        {
+            pugi::xml_node include = child;
+
+            // load new preprocessed document (note: ideally this should handle relative paths)
+            const char* path = include.value();
+
+            pugi::xml_document doc;
+            if (!load_preprocess(doc, path)) return false;
+
+            // insert the comment marker above include directive
+            node.insert_child_before(pugi::node_comment, include).set_value(path);
+
+            // copy the document above the include directive (this retains the original order!)
+            for (pugi::xml_node ic = doc.first_child(); ic; ic = ic.next_sibling())
+            {
+                node.insert_copy_before(ic, include);
+            }
+
+            // remove the include node and move to the next child
+            child = child.next_sibling();
+
+            node.remove_child(include);
+        }
+        else
+        {
+            if (!preprocess(child)) return false;
+
+            child = child.next_sibling();
+        }
+    }
+
+    return true;
+}
+
+bool load_preprocess(pugi::xml_document& doc, const char* path)
+{
+    pugi::xml_parse_result result = doc.load_file(path, pugi::parse_default | pugi::parse_pi); // for <?include?>
+    
+    return result ? preprocess(doc) : false;
+}
+
+

+

+
+
+ + + +
+
+ + + +
+pugixml 1.0 manual | + Overview | + Installation | + Document: + Object model · Loading · Accessing · Modifying · Saving | + XPath | + API Reference | + Table of Contents +
+PrevUpHomeNext +
+ + diff --git a/LifeGraphics/pugixml/docs/manual/saving.html b/LifeGraphics/pugixml/docs/manual/saving.html new file mode 100644 index 0000000..abaf9f2 --- /dev/null +++ b/LifeGraphics/pugixml/docs/manual/saving.html @@ -0,0 +1,463 @@ + + + +Saving document + + + + + + + + + + + +
+pugixml 1.0 manual | + Overview | + Installation | + Document: + Object model · Loading · Accessing · Modifying · Saving | + XPath | + API Reference | + Table of Contents +
+PrevUpHomeNext +
+
+
+ + +

+ Often after creating a new document or loading the existing one and processing + it, it is necessary to save the result back to file. Also it is occasionally + useful to output the whole document or a subtree to some stream; use cases + include debug printing, serialization via network or other text-oriented medium, + etc. pugixml provides several functions to output any subtree of the document + to a file, stream or another generic transport interface; these functions allow + to customize the output format (see Output options), and also perform + necessary encoding conversions (see Encodings). This section documents + the relevant functionality. +

+

+ Before writing to the destination the node/attribute data is properly formatted + according to the node type; all special XML symbols, such as < and &, + are properly escaped. In order to guard against forgotten node/attribute names, + empty node/attribute names are printed as ":anonymous". + For well-formed output, make sure all node and attribute names are set to meaningful + values. +

+

+ CDATA sections with values that contain "]]>" + are split into several sections as follows: section with value "pre]]>post" is written as <![CDATA[pre]]]]><![CDATA[>post]]>. + While this alters the structure of the document (if you load the document after + saving it, there will be two CDATA sections instead of one), this is the only + way to escape CDATA contents. +

+
+ +

+ If you want to save the whole document to a file, you can use one of the + following functions: +

+
bool xml_document::save_file(const char* path, const char_t* indent = "\t", unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;
+bool xml_document::save_file(const wchar_t* path, const char_t* indent = "\t", unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;
+
+

+ These functions accept file path as its first argument, and also three optional + arguments, which specify indentation and other output options (see Output options) + and output data encoding (see Encodings). The path has the target + operating system format, so it can be a relative or absolute one, it should + have the delimiters of the target system, it should have the exact case if + the target file system is case-sensitive, etc. +

+

+ File path is passed to the system file opening function as is in case of + the first function (which accepts const + char* path); the second function either uses + a special file opening function if it is provided by the runtime library + or converts the path to UTF-8 and uses the system file opening function. +

+

+ save_file opens the target + file for writing, outputs the requested header (by default a document declaration + is output, unless the document already has one), and then saves the document + contents. If the file could not be opened, the function returns false. Calling save_file + is equivalent to creating an xml_writer_file + object with FILE* + handle as the only constructor argument and then calling save; + see Saving document via writer interface for writer interface details. +

+

+ This is a simple example of saving XML document to file (samples/save_file.cpp): +

+

+ +

+
// save document to file
+std::cout << "Saving result: " << doc.save_file("save_file_output.xml") << std::endl;
+
+

+

+
+
+ +

+ To enhance interoperability pugixml provides functions for saving document + to any object which implements C++ std::ostream + interface. This allows you to save documents to any standard C++ stream (i.e. + file stream) or any third-party compliant implementation (i.e. Boost Iostreams). + Most notably, this allows for easy debug output, since you can use std::cout + stream as saving target. There are two functions, one works with narrow character + streams, another handles wide character ones: +

+
void xml_document::save(std::ostream& stream, const char_t* indent = "\t", unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;
+void xml_document::save(std::wostream& stream, const char_t* indent = "\t", unsigned int flags = format_default) const;
+
+

+ save with std::ostream + argument saves the document to the stream in the same way as save_file (i.e. with requested header and + with encoding conversions). On the other hand, save + with std::wstream argument saves the document to + the wide stream with encoding_wchar + encoding. Because of this, using save + with wide character streams requires careful (usually platform-specific) + stream setup (i.e. using the imbue + function). Generally use of wide streams is discouraged, however it provides + you with the ability to save documents to non-Unicode encodings, i.e. you + can save Shift-JIS encoded data if you set the correct locale. +

+

+ Calling save with stream + target is equivalent to creating an xml_writer_stream + object with stream as the only constructor argument and then calling save; see Saving document via writer interface for writer + interface details. +

+

+ This is a simple example of saving XML document to standard output (samples/save_stream.cpp): +

+

+ +

+
// save document to standard output
+std::cout << "Document:\n";
+doc.save(std::cout);
+
+

+

+
+
+ +

+ All of the above saving functions are implemented in terms of writer interface. + This is a simple interface with a single function, which is called several + times during output process with chunks of document data as input: +

+
class xml_writer
+{
+public:
+    virtual void write(const void* data, size_t size) = 0;
+};
+
+void xml_document::save(xml_writer& writer, const char_t* indent = "\t", unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;
+
+

+ In order to output the document via some custom transport, for example sockets, + you should create an object which implements xml_writer_file + interface and pass it to save + function. xml_writer_file::write + function is called with a buffer as an input, where data + points to buffer start, and size + is equal to the buffer size in bytes. write + implementation must write the buffer to the transport; it can not save the + passed buffer pointer, as the buffer contents will change after write returns. The buffer contains the + chunk of document data in the desired encoding. +

+

+ write function is called + with relatively large blocks (size is usually several kilobytes, except for + the first block with BOM, which is output only if format_write_bom + is set, and last block, which may be small), so there is often no need for + additional buffering in the implementation. +

+

+ This is a simple example of custom writer for saving document data to STL + string (samples/save_custom_writer.cpp); + read the sample code for more complex examples: +

+

+ +

+
struct xml_string_writer: pugi::xml_writer
+{
+    std::string result;
+
+    virtual void write(const void* data, size_t size)
+    {
+        result += std::string(static_cast<const char*>(data), size);
+    }
+};
+
+

+

+
+
+ +

+ While the previously described functions save the whole document to the destination, + it is easy to save a single subtree. The following functions are provided: +

+
void xml_node::print(std::ostream& os, const char_t* indent = "\t", unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const;
+void xml_node::print(std::wostream& os, const char_t* indent = "\t", unsigned int flags = format_default, unsigned int depth = 0) const;
+void xml_node::print(xml_writer& writer, const char_t* indent = "\t", unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const;
+
+

+ These functions have the same arguments with the same meaning as the corresponding + xml_document::save functions, and allow you to save the + subtree to either a C++ IOstream or to any object that implements xml_writer interface. +

+

+ Saving a subtree differs from saving the whole document: the process behaves + as if format_write_bom is off, and + format_no_declaration is on, + even if actual values of the flags are different. This means that BOM is + not written to the destination, and document declaration is only written + if it is the node itself or is one of node's children. Note that this also + holds if you're saving a document; this example (samples/save_subtree.cpp) + illustrates the difference: +

+

+ +

+
// get a test document
+pugi::xml_document doc;
+doc.load("<foo bar='baz'><call>hey</call></foo>");
+
+// print document to standard output (prints <?xml version="1.0"?><foo bar="baz"><call>hey</call></foo>)
+doc.save(std::cout, "", pugi::format_raw);
+std::cout << std::endl;
+
+// print document to standard output as a regular node (prints <foo bar="baz"><call>hey</call></foo>)
+doc.print(std::cout, "", pugi::format_raw);
+std::cout << std::endl;
+
+// print a subtree to standard output (prints <call>hey</call>)
+doc.child("foo").child("call").print(std::cout, "", pugi::format_raw);
+std::cout << std::endl;
+
+

+

+
+
+ +

+ All saving functions accept the optional parameter flags. + This is a bitmask that customizes the output format; you can select the way + the document nodes are printed and select the needed additional information + that is output before the document contents. +

+
+ + + + + +
[Note]Note

+ You should use the usual bitwise arithmetics to manipulate the bitmask: + to enable a flag, use mask | flag; + to disable a flag, use mask & ~flag. +

+

+ These flags control the resulting tree contents: +

+
    +
  • + format_indent determines if all nodes + should be indented with the indentation string (this is an additional + parameter for all saving functions, and is "\t" + by default). If this flag is on, before every node the indentation string + is output several times, where the amount of indentation depends on the + node's depth relative to the output subtree. This flag has no effect + if format_raw is enabled. This flag + is on by default.

    + +
  • +
  • + format_raw switches between formatted and + raw output. If this flag is on, the nodes are not indented in any way, + and also no newlines that are not part of document text are printed. + Raw mode can be used for serialization where the result is not intended + to be read by humans; also it can be useful if the document was parsed + with parse_ws_pcdata flag, to + preserve the original document formatting as much as possible. This flag + is off by default. +
  • +
+

+ These flags control the additional output information: +

+
    +
  • + format_no_declaration disables + default node declaration output. By default, if the document is saved + via save or save_file function, and it does not + have any document declaration, a default declaration is output before + the document contents. Enabling this flag disables this declaration. + This flag has no effect in xml_node::print + functions: they never output the default declaration. This flag is off by default.

    + +
  • +
  • + format_write_bom enables Byte Order + Mark (BOM) output. By default, no BOM is output, so in case of non UTF-8 + encodings the resulting document's encoding may not be recognized by + some parsers and text editors, if they do not implement sophisticated + encoding detection. Enabling this flag adds an encoding-specific BOM + to the output. This flag has no effect in xml_node::print + functions: they never output the BOM. This flag is off + by default. +
  • +
+

+ Additionally, there is one predefined option mask: +

+
  • + format_default is the default set of + flags, i.e. it has all options set to their default values. It sets formatted + output with indentation, without BOM and with default node declaration, + if necessary. +
+

+ This is an example that shows the outputs of different output options (samples/save_options.cpp): +

+

+ +

+
// get a test document
+pugi::xml_document doc;
+doc.load("<foo bar='baz'><call>hey</call></foo>");
+
+// default options; prints
+// <?xml version="1.0"?>
+// <foo bar="baz">
+//         <call>hey</call>
+// </foo>
+doc.save(std::cout);
+std::cout << std::endl;
+
+// default options with custom indentation string; prints
+// <?xml version="1.0"?>
+// <foo bar="baz">
+// --<call>hey</call>
+// </foo>
+doc.save(std::cout, "--");
+std::cout << std::endl;
+
+// default options without indentation; prints
+// <?xml version="1.0"?>
+// <foo bar="baz">
+// <call>hey</call>
+// </foo>
+doc.save(std::cout, "\t", pugi::format_default & ~pugi::format_indent); // can also pass "" instead of indentation string for the same effect
+std::cout << std::endl;
+
+// raw output; prints
+// <?xml version="1.0"?><foo bar="baz"><call>hey</call></foo>
+doc.save(std::cout, "\t", pugi::format_raw);
+std::cout << std::endl << std::endl;
+
+// raw output without declaration; prints
+// <foo bar="baz"><call>hey</call></foo>
+doc.save(std::cout, "\t", pugi::format_raw | pugi::format_no_declaration);
+std::cout << std::endl;
+
+

+

+
+
+ +

+ pugixml supports all popular Unicode encodings (UTF-8, UTF-16 (big and little + endian), UTF-32 (big and little endian); UCS-2 is naturally supported since + it's a strict subset of UTF-16) and handles all encoding conversions during + output. The output encoding is set via the encoding + parameter of saving functions, which is of type xml_encoding. + The possible values for the encoding are documented in Encodings; + the only flag that has a different meaning is encoding_auto. +

+

+ While all other flags set the exact encoding, encoding_auto + is meant for automatic encoding detection. The automatic detection does not + make sense for output encoding, since there is usually nothing to infer the + actual encoding from, so here encoding_auto + means UTF-8 encoding, which is the most popular encoding for XML data storage. + This is also the default value of output encoding; specify another value + if you do not want UTF-8 encoded output. +

+

+ Also note that wide stream saving functions do not have encoding + argument and always assume encoding_wchar + encoding. +

+
+ + + + + +
[Note]Note

+ The current behavior for Unicode conversion is to skip all invalid UTF + sequences during conversion. This behavior should not be relied upon; if + your node/attribute names do not contain any valid UTF sequences, they + may be output as if they are empty, which will result in malformed XML + document. +

+
+
+ + + +
+
+ + + +
+pugixml 1.0 manual | + Overview | + Installation | + Document: + Object model · Loading · Accessing · Modifying · Saving | + XPath | + API Reference | + Table of Contents +
+PrevUpHomeNext +
+ + diff --git a/LifeGraphics/pugixml/docs/manual/toc.html b/LifeGraphics/pugixml/docs/manual/toc.html new file mode 100644 index 0000000..97d0b6c --- /dev/null +++ b/LifeGraphics/pugixml/docs/manual/toc.html @@ -0,0 +1,153 @@ + + + +Table of Contents + + + + + + + + + + +
+pugixml 1.0 manual | + Overview | + Installation | + Document: + Object model · Loading · Accessing · Modifying · Saving | + XPath | + API Reference | + Table of Contents +
+PrevUpHome +
+
+
+ +
+
Overview
+
+
Introduction
+
Feedback
+
Acknowledgments
+
License
+
+
Installation
+
+
Getting pugixml
+
+
Source distributions
+
Subversion repository
+
+
Building pugixml
+
+
Building pugixml as + a part of another static library/executable
+
Building pugixml as + a standalone static library
+
Building pugixml as + a standalone shared library
+
Additional configuration + options
+
+
Portability
+
+
Document object model
+
+
Tree structure
+
C++ interface
+
Unicode interface
+
Thread-safety guarantees
+
Exception guarantees
+
Memory management
+
+
Custom memory allocation/deallocation + functions
+
Document memory management + internals
+
+
+
Loading document
+
+
Loading document from file
+
Loading document from memory
+
Loading document from C++ IOstreams
+
Handling parsing errors
+
Parsing options
+
Encodings
+
Conformance to W3C specification
+
+
Accessing document data
+
+
Basic traversal functions
+
Getting node data
+
Getting attribute data
+
Contents-based traversal functions
+
Traversing node/attribute lists + via iterators
+
Recursive traversal with xml_tree_walker
+
Searching for nodes/attributes + with predicates
+
Miscellaneous functions
+
+
Modifying document data
+
+
Setting node data
+
Setting attribute data
+
Adding nodes/attributes
+
Removing nodes/attributes
+
Cloning nodes/attributes
+
+
Saving document
+
+
Saving document to a file
+
Saving document to C++ IOstreams
+
Saving document via writer interface
+
Saving a single subtree
+
Output options
+
Encodings
+
+
XPath
+
+
XPath types
+
Selecting nodes via XPath expression
+
Using query objects
+
Using variables
+
Error handling
+
Conformance to W3C specification
+
+
Changelog
+
API Reference
+
Table of Contents
+
+
+ + + +
+
+ + + +
+pugixml 1.0 manual | + Overview | + Installation | + Document: + Object model · Loading · Accessing · Modifying · Saving | + XPath | + API Reference | + Table of Contents +
+PrevUpHome +
+ + diff --git a/LifeGraphics/pugixml/docs/manual/xpath.html b/LifeGraphics/pugixml/docs/manual/xpath.html new file mode 100644 index 0000000..ea6b956 --- /dev/null +++ b/LifeGraphics/pugixml/docs/manual/xpath.html @@ -0,0 +1,742 @@ + + + +XPath + + + + + + + + + + + +
+pugixml 1.0 manual | + Overview | + Installation | + Document: + Object model · Loading · Accessing · Modifying · Saving | + XPath | + API Reference | + Table of Contents +
+PrevUpHomeNext +
+
+
+

+ XPath +

+ +

+ If the task at hand is to select a subset of document nodes that match some + criteria, it is possible to code a function using the existing traversal functionality + for any practical criteria. However, often either a data-driven approach is + desirable, in case the criteria are not predefined and come from a file, or + it is inconvenient to use traversal interfaces and a higher-level DSL is required. + There is a standard language for XML processing, XPath, that can be useful + for these cases. pugixml implements an almost complete subset of XPath 1.0. + Because of differences in document object model and some performance implications, + there are minor violations of the official specifications, which can be found + in Conformance to W3C specification. The rest of this section describes the interface for XPath + functionality. Please note that if you wish to learn to use XPath language, + you have to look for other tutorials or manuals; for example, you can read + W3Schools XPath tutorial, + XPath tutorial + at tizag.com, and the XPath + 1.0 specification. +

+
+ +

+ Each XPath expression can have one of the following types: boolean, number, + string or node set. Boolean type corresponds to bool + type, number type corresponds to double + type, string type corresponds to either std::string + or std::wstring, depending on whether wide + character interface is enabled, and node set corresponds to xpath_node_set type. There is an enumeration, + xpath_value_type, which can + take the values xpath_type_boolean, + xpath_type_number, xpath_type_string or xpath_type_node_set, + accordingly. +

+

+ Because an XPath node can be either a node or an attribute, there is a special + type, xpath_node, which is + a discriminated union of these types. A value of this type contains two node + handles, one of xml_node + type, and another one of xml_attribute + type; at most one of them can be non-null. The accessors to get these handles + are available: +

+
xml_node xpath_node::node() const;
+xml_attribute xpath_node::attribute() const;
+
+

+ XPath nodes can be null, in which case both accessors return null handles. +

+

+ Note that as per XPath specification, each XPath node has a parent, which + can be retrieved via this function: +

+
xml_node xpath_node::parent() const;
+
+

+ parent function returns the + node's parent if the XPath node corresponds to xml_node + handle (equivalent to node().parent()), or the node to which the attribute belongs + to, if the XPath node corresponds to xml_attribute + handle. For null nodes, parent + returns null handle. +

+

+ Like node and attribute handles, XPath node handles can be implicitly cast + to boolean-like object to check if it is a null node, and also can be compared + for equality with each other. +

+

+ You can also create XPath nodes with one of the three constructors: the default + constructor, the constructor that takes node argument, and the constructor + that takes attribute and node arguments (in which case the attribute must + belong to the attribute list of the node). The constructor from xml_node is implicit, so you can usually + pass xml_node to functions + that expect xpath_node. Apart + from that you usually don't need to create your own XPath node objects, since + they are returned to you via selection functions. +

+

+ XPath expressions operate not on single nodes, but instead on node sets. + A node set is a collection of nodes, which can be optionally ordered in either + a forward document order or a reverse one. Document order is defined in XPath + specification; an XPath node is before another node in document order if + it appears before it in XML representation of the corresponding document. +

+

+ Node sets are represented by xpath_node_set + object, which has an interface that resembles one of sequential random-access + containers. It has an iterator type along with usual begin/past-the-end iterator + accessors: +

+
typedef const xpath_node* xpath_node_set::const_iterator;
+const_iterator xpath_node_set::begin() const;
+const_iterator xpath_node_set::end() const;
+
+

+ And it also can be iterated via indices, just like std::vector: +

+
const xpath_node& xpath_node_set::operator[](size_t index) const;
+size_t xpath_node_set::size() const;
+bool xpath_node_set::empty() const;
+
+

+ All of the above operations have the same semantics as that of std::vector: + the iterators are random-access, all of the above operations are constant + time, and accessing the element at index that is greater or equal than the + set size results in undefined behavior. You can use both iterator-based and + index-based access for iteration, however the iterator-based one can be faster. +

+

+ The order of iteration depends on the order of nodes inside the set; the + order can be queried via the following function: +

+
enum xpath_node_set::type_t {type_unsorted, type_sorted, type_sorted_reverse};
+type_t xpath_node_set::type() const;
+
+

+ type function returns the + current order of nodes; type_sorted + means that the nodes are in forward document order, type_sorted_reverse + means that the nodes are in reverse document order, and type_unsorted + means that neither order is guaranteed (nodes can accidentally be in a sorted + order even if type() + returns type_unsorted). If + you require a specific order of iteration, you can change it via sort function: +

+
void xpath_node_set::sort(bool reverse = false);
+
+

+ Calling sort sorts the nodes + in either forward or reverse document order, depending on the argument; after + this call type() + will return type_sorted or + type_sorted_reverse. +

+

+ Often the actual iteration is not needed; instead, only the first element + in document order is required. For this, a special accessor is provided: +

+
xpath_node xpath_node_set::first() const;
+
+

+ This function returns the first node in forward document order from the set, + or null node if the set is empty. Note that while the result of the node + does not depend on the order of nodes in the set (i.e. on the result of + type()), + the complexity does - if the set is sorted, the complexity is constant, otherwise + it is linear in the number of elements or worse. +

+

+ While in the majority of cases the node set is returned by XPath functions, + sometimes there is a need to manually construct a node set. For such cases, + a constructor is provided which takes an iterator range (const_iterator + is a typedef for const xpath_node*), and an optional type: +

+
xpath_node_set::xpath_node_set(const_iterator begin, const_iterator end, type_t type = type_unsorted);
+
+

+ The constructor copies the specified range and sets the specified type. The + objects in the range are not checked in any way; you'll have to ensure that + the range contains no duplicates, and that the objects are sorted according + to the type parameter. Otherwise + XPath operations with this set may produce unexpected results. +

+
+
+ +

+ If you want to select nodes that match some XPath expression, you can do + it with the following functions: +

+
xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables = 0) const;
+xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables = 0) const;
+
+

+ select_nodes function compiles + the expression and then executes it with the node as a context node, and + returns the resulting node set. select_single_node + returns only the first node in document order from the result, and is equivalent + to calling select_nodes(query).first(). + If the XPath expression does not match anything, or the node handle is null, + select_nodes returns an empty + set, and select_single_node + returns null XPath node. +

+

+ If exception handling is not disabled, both functions throw xpath_exception + if the query can not be compiled or if it returns a value with type other + than node set; see Error handling for details. +

+

+ While compiling expressions is fast, the compilation time can introduce a + significant overhead if the same expression is used many times on small subtrees. + If you're doing many similar queries, consider compiling them into query + objects (see Using query objects for further reference). Once you get a compiled + query object, you can pass it to select functions instead of an expression + string: +

+
xpath_node xml_node::select_single_node(const xpath_query& query) const;
+xpath_node_set xml_node::select_nodes(const xpath_query& query) const;
+
+

+ If exception handling is not disabled, both functions throw xpath_exception + if the query returns a value with type other than node set. +

+

+ This is an example of selecting nodes using XPath expressions (samples/xpath_select.cpp): +

+

+ +

+
pugi::xpath_node_set tools = doc.select_nodes("/Profile/Tools/Tool[@AllowRemote='true' and @DeriveCaptionFrom='lastparam']");
+
+std::cout << "Tools:";
+
+for (pugi::xpath_node_set::const_iterator it = tools.begin(); it != tools.end(); ++it)
+{
+    pugi::xpath_node node = *it;
+    std::cout << " " << node.node().attribute("Filename").value();
+}
+
+pugi::xpath_node build_tool = doc.select_single_node("//Tool[contains(Description, 'build system')]");
+
+std::cout << "\nBuild tool: " << build_tool.node().attribute("Filename").value() << "\n";
+
+

+

+
+
+ +

+ When you call select_nodes + with an expression string as an argument, a query object is created behind + the scenes. A query object represents a compiled XPath expression. Query + objects can be needed in the following circumstances: +

+
    +
  • + You can precompile expressions to query objects to save compilation time + if it becomes an issue; +
  • +
  • + You can use query objects to evaluate XPath expressions which result + in booleans, numbers or strings; +
  • +
  • + You can get the type of expression value via query object. +
  • +
+

+ Query objects correspond to xpath_query + type. They are immutable and non-copyable: they are bound to the expression + at creation time and can not be cloned. If you want to put query objects + in a container, allocate them on heap via new + operator and store pointers to xpath_query + in the container. +

+

+ You can create a query object with the constructor that takes XPath expression + as an argument: +

+
explicit xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables = 0);
+
+

+ The expression is compiled and the compiled representation is stored in the + new query object. If compilation fails, xpath_exception + is thrown if exception handling is not disabled (see Error handling for + details). After the query is created, you can query the type of the evaluation + result using the following function: +

+
xpath_value_type xpath_query::return_type() const;
+
+

+ You can evaluate the query using one of the following functions: +

+
bool xpath_query::evaluate_boolean(const xpath_node& n) const;
+double xpath_query::evaluate_number(const xpath_node& n) const;
+string_t xpath_query::evaluate_string(const xpath_node& n) const;
+xpath_node_set xpath_query::evaluate_node_set(const xpath_node& n) const;
+
+

+ All functions take the context node as an argument, compute the expression + and return the result, converted to the requested type. According to XPath + specification, value of any type can be converted to boolean, number or string + value, but no type other than node set can be converted to node set. Because + of this, evaluate_boolean, + evaluate_number and evaluate_string always return a result, + but evaluate_node_set results + in an error if the return type is not node set (see Error handling). +

+
+ + + + + +
[Note]Note

+ Calling node.select_nodes("query") + is equivalent to calling xpath_query("query").evaluate_node_set(node). +

+

+ Note that evaluate_string + function returns the STL string; as such, it's not available in PUGIXML_NO_STL + mode and also usually allocates memory. There is another string evaluation + function: +

+
size_t xpath_query::evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const;
+
+

+ This function evaluates the string, and then writes the result to buffer (but at most capacity + characters); then it returns the full size of the result in characters, including + the terminating zero. If capacity + is not 0, the resulting buffer is always zero-terminated. You can use this + function as follows: +

+
    +
  • + First call the function with buffer + = 0 + and capacity = + 0; then allocate the returned amount + of characters, and call the function again, passing the allocated storage + and the amount of characters; +
  • +
  • + First call the function with small buffer and buffer capacity; then, + if the result is larger than the capacity, the output has been trimmed, + so allocate a larger buffer and call the function again. +
  • +
+

+ This is an example of using query objects (samples/xpath_query.cpp): +

+

+ +

+
// Select nodes via compiled query
+pugi::xpath_query query_remote_tools("/Profile/Tools/Tool[@AllowRemote='true']");
+
+pugi::xpath_node_set tools = query_remote_tools.evaluate_node_set(doc);
+std::cout << "Remote tool: ";
+tools[2].node().print(std::cout);
+
+// Evaluate numbers via compiled query
+pugi::xpath_query query_timeouts("sum(//Tool/@Timeout)");
+std::cout << query_timeouts.evaluate_number(doc) << std::endl;
+
+// Evaluate strings via compiled query for different context nodes
+pugi::xpath_query query_name_valid("string-length(substring-before(@Filename, '_')) > 0 and @OutputFileMasks");
+pugi::xpath_query query_name("concat(substring-before(@Filename, '_'), ' produces ', @OutputFileMasks)");
+
+for (pugi::xml_node tool = doc.first_element_by_path("Profile/Tools/Tool"); tool; tool = tool.next_sibling())
+{
+    std::string s = query_name.evaluate_string(tool);
+
+    if (query_name_valid.evaluate_boolean(tool)) std::cout << s << std::endl;
+}
+
+

+

+
+
+ +

+ XPath queries may contain references to variables; this is useful if you + want to use queries that depend on some dynamic parameter without manually + preparing the complete query string, or if you want to reuse the same query + object for similar queries. +

+

+ Variable references have the form $name; in order to use them, you have to provide + a variable set, which includes all variables present in the query with correct + types. This set is passed to xpath_query + constructor or to select_nodes/select_single_node functions: +

+
explicit xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables = 0);
+xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables = 0) const;
+xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables = 0) const;
+
+

+ If you're using query objects, you can change the variable values before + evaluate/select + calls to change the query behavior. +

+
+ + + + + +
[Note]Note

+ The variable set pointer is stored in the query object; you have to ensure + that the lifetime of the set exceeds that of query object. +

+

+ Variable sets correspond to xpath_variable_set + type, which is essentially a variable container. +

+

+ You can add new variables with the following function: +

+
xpath_variable* xpath_variable_set::add(const char_t* name, xpath_value_type type);
+
+

+ The function tries to add a new variable with the specified name and type; + if the variable with such name does not exist in the set, the function adds + a new variable and returns the variable handle; if there is already a variable + with the specified name, the function returns the variable handle if variable + has the specified type. Otherwise the function returns null pointer; it also + returns null pointer on allocation failure. +

+

+ New variables are assigned the default value which depends on the type: + 0 for numbers, false for booleans, empty string for strings + and empty set for node sets. +

+

+ You can get the existing variables with the following functions: +

+
xpath_variable* xpath_variable_set::get(const char_t* name);
+const xpath_variable* xpath_variable_set::get(const char_t* name) const;
+
+

+ The functions return the variable handle, or null pointer if the variable + with the specified name is not found. +

+

+ Additionally, there are the helper functions for setting the variable value + by name; they try to add the variable with the corresponding type, if it + does not exist, and to set the value. If the variable with the same name + but with different type is already present, they return false; + they also return false on allocation + failure. Note that these functions do not perform any type conversions. +

+
bool xpath_variable_set::set(const char_t* name, bool value);
+bool xpath_variable_set::set(const char_t* name, double value);
+bool xpath_variable_set::set(const char_t* name, const char_t* value);
+bool xpath_variable_set::set(const char_t* name, const xpath_node_set& value);
+
+

+ The variable values are copied to the internal variable storage, so you can + modify or destroy them after the functions return. +

+

+ If setting variables by name is not efficient enough, or if you have to inspect + variable information or get variable values, you can use variable handles. + A variable corresponds to the xpath_variable + type, and a variable handle is simply a pointer to xpath_variable. +

+

+ In order to get variable information, you can use one of the following functions: +

+
const char_t* xpath_variable::name() const;
+xpath_value_type xpath_variable::type() const;
+
+

+ Note that each variable has a distinct type which is specified upon variable + creation and can not be changed later. +

+

+ In order to get variable value, you should use one of the following functions, + depending on the variable type: +

+
bool xpath_variable::get_boolean() const;
+double xpath_variable::get_number() const;
+const char_t* xpath_variable::get_string() const;
+const xpath_node_set& xpath_variable::get_node_set() const;
+
+

+ These functions return the value of the variable. Note that no type conversions + are performed; if the type mismatch occurs, a dummy value is returned (false for booleans, NaN + for numbers, empty string for strings and empty set for node sets). +

+

+ In order to set variable value, you should use one of the following functions, + depending on the variable type: +

+
bool xpath_variable::set(bool value);
+bool xpath_variable::set(double value);
+bool xpath_variable::set(const char_t* value);
+bool xpath_variable::set(const xpath_node_set& value);
+
+

+ These functions modify the variable value. Note that no type conversions + are performed; if the type mismatch occurs, the functions return false; they also return false + on allocation failure. The variable values are copied to the internal variable + storage, so you can modify or destroy them after the functions return. +

+

+ This is an example of using variables in XPath queries (samples/xpath_variables.cpp): +

+

+ +

+
// Select nodes via compiled query
+pugi::xpath_variable_set vars;
+vars.add("remote", pugi::xpath_type_boolean);
+
+pugi::xpath_query query_remote_tools("/Profile/Tools/Tool[@AllowRemote = string($remote)]", &vars);
+
+vars.set("remote", true);
+pugi::xpath_node_set tools_remote = query_remote_tools.evaluate_node_set(doc);
+
+vars.set("remote", false);
+pugi::xpath_node_set tools_local = query_remote_tools.evaluate_node_set(doc);
+
+std::cout << "Remote tool: ";
+tools_remote[2].node().print(std::cout);
+
+std::cout << "Local tool: ";
+tools_local[0].node().print(std::cout);
+
+// You can pass the context directly to select_nodes/select_single_node
+pugi::xpath_node_set tools_local_imm = doc.select_nodes("/Profile/Tools/Tool[@AllowRemote = string($remote)]", &vars);
+
+std::cout << "Local tool imm: ";
+tools_local_imm[0].node().print(std::cout);
+
+

+

+
+
+ +

+ There are two different mechanisms for error handling in XPath implementation; + the mechanism used depends on whether exception support is disabled (this + is controlled with PUGIXML_NO_EXCEPTIONS + define). +

+

+ By default, XPath functions throw xpath_exception + object in case of errors; additionally, in the event any memory allocation + fails, an std::bad_alloc exception is thrown. Also xpath_exception is thrown if the query + is evaluated to a node set, but the return type is not node set. If the query + constructor succeeds (i.e. no exception is thrown), the query object is valid. + Otherwise you can get the error details via one of the following functions: +

+
virtual const char* xpath_exception::what() const throw();
+const xpath_parse_result& xpath_exception::result() const;
+
+

+ If exceptions are disabled, then in the event of parsing failure the query + is initialized to invalid state; you can test if the query object is valid + by using it in a boolean expression: if + (query) { ... + }. Additionally, you can get parsing + result via the result() accessor: +

+
const xpath_parse_result& xpath_query::result() const;
+
+

+ Without exceptions, evaluating invalid query results in false, + empty string, NaN or an empty node set, depending on the type; evaluating + a query as a node set results in an empty node set if the return type is + not node set. +

+

+ The information about parsing result is returned via xpath_parse_result + object. It contains parsing status and the offset of last successfully parsed + character from the beginning of the source stream: +

+
struct xpath_parse_result
+{
+    const char* error;
+    ptrdiff_t offset;
+
+    operator bool() const;
+    const char* description() const;
+};
+
+

+ Parsing result is represented as the error message; it is either a null pointer, + in case there is no error, or the error message in the form of ASCII zero-terminated + string. +

+

+ description() + member function can be used to get the error message; it never returns the + null pointer, so you can safely use description() even if query parsing succeeded. +

+

+ In addition to the error message, parsing result has an offset + member, which contains the offset of last successfully parsed character. + This offset is in units of pugi::char_t (bytes + for character mode, wide characters for wide character mode). +

+

+ Parsing result object can be implicitly converted to bool + like this: if (result) { ... } + else { ... }. +

+

+ This is an example of XPath error handling (samples/xpath_error.cpp): +

+

+ +

+
// Exception is thrown for incorrect query syntax
+try
+{
+    doc.select_nodes("//nodes[#true()]");
+}
+catch (const pugi::xpath_exception& e)
+{
+    std::cout << "Select failed: " << e.what() << std::endl;
+}
+
+// Exception is thrown for incorrect query semantics
+try
+{
+    doc.select_nodes("(123)/next");
+}
+catch (const pugi::xpath_exception& e)
+{
+    std::cout << "Select failed: " << e.what() << std::endl;
+}
+
+// Exception is thrown for query with incorrect return type
+try
+{
+    doc.select_nodes("123");
+}
+catch (const pugi::xpath_exception& e)
+{
+    std::cout << "Select failed: " << e.what() << std::endl;
+}
+
+

+

+
+
+ +

+ Because of the differences in document object models, performance considerations + and implementation complexity, pugixml does not provide a fully conformant + XPath 1.0 implementation. This is the current list of incompatibilities: +

+
    +
  • + Consecutive text nodes sharing the same parent are not merged, i.e. in + <node>text1 + <![CDATA[data]]> text2</node> node should have one text node children, + but instead has three. +
  • +
  • + Since the document type declaration is not used for parsing, id() + function always returns an empty node set. +
  • +
  • + Namespace nodes are not supported (affects namespace:: axis). +
  • +
  • + Name tests are performed on QNames in XML document instead of expanded + names; for <foo + xmlns:ns1='uri' xmlns:ns2='uri'><ns1:child/><ns2:child/></foo>, + query foo/ns1:* + will return only the first child, not both of them. Compliant XPath implementations + can return both nodes if the user provides appropriate namespace declarations. +
  • +
  • + String functions consider a character to be either a single char value or a single wchar_t + value, depending on the library configuration; this means that some string + functions are not fully Unicode-aware. This affects substring(), string-length() and translate() functions. +
  • +
+
+
+ + + +
+
+ + + +
+pugixml 1.0 manual | + Overview | + Installation | + Document: + Object model · Loading · Accessing · Modifying · Saving | + XPath | + API Reference | + Table of Contents +
+PrevUpHomeNext +
+ + diff --git a/LifeGraphics/pugixml/docs/pugixml.css b/LifeGraphics/pugixml/docs/pugixml.css new file mode 100644 index 0000000..0a72f78 --- /dev/null +++ b/LifeGraphics/pugixml/docs/pugixml.css @@ -0,0 +1,598 @@ +/*============================================================================= + Copyright (c) 2004 Joel de Guzman + http://spirit.sourceforge.net/ + + Distributed under the Boost Software License, Version 1.0. (See accompany- + ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +/*============================================================================= + Body defaults +=============================================================================*/ + + body + { + margin: 1em; + font-family: sans-serif; + } + +/*============================================================================= + Paragraphs +=============================================================================*/ + + p + { + text-align: left; + font-size: 10pt; + line-height: 1.15; + } + +/*============================================================================= + Program listings +=============================================================================*/ + + /* Code on paragraphs */ + p tt.computeroutput + { + font-size: 9pt; + } + + pre.synopsis + { + font-size: 90%; + margin: 1pc 4% 0pc 4%; + padding: 0.5pc 0.5pc 0.5pc 0.5pc; + } + + .programlisting, + .screen + { + font-size: 9pt; + display: block; + margin: 1pc 4% 0pc 4%; + padding: 0.5pc 0.5pc 0.5pc 0.5pc; + } + + /* Program listings in tables don't get borders */ + td .programlisting, + td .screen + { + margin: 0pc 0pc 0pc 0pc; + padding: 0pc 0pc 0pc 0pc; + } + +/*============================================================================= + Headings +=============================================================================*/ + + h1, h2, h3, h4, h5, h6 + { + text-align: left; + margin: 1em 0em 0.5em 0em; + font-weight: bold; + } + + h1 { font: 140% } + h2 { font: bold 140% } + h3 { font: bold 130% } + h4 { font: bold 120% } + h5 { font: italic 110% } + h6 { font: italic 100% } + + /* Top page titles */ + title, + h1.title, + h2.title + h3.title, + h4.title, + h5.title, + h6.title, + .refentrytitle + { + font-weight: bold; + margin-bottom: 1pc; + } + + h1.title { font-size: 140% } + h2.title { font-size: 140% } + h3.title { font-size: 130% } + h4.title { font-size: 120% } + h5.title { font-size: 110% } + h6.title { font-size: 100% } + + .section h1 + { + margin: 0em 0em 0.5em 0em; + font-size: 140%; + } + + .section h2 { font-size: 140% } + .section h3 { font-size: 130% } + .section h4 { font-size: 120% } + .section h5 { font-size: 110% } + .section h6 { font-size: 100% } + + /* Code on titles */ + h1 tt.computeroutput { font-size: 140% } + h2 tt.computeroutput { font-size: 140% } + h3 tt.computeroutput { font-size: 130% } + h4 tt.computeroutput { font-size: 130% } + h5 tt.computeroutput { font-size: 130% } + h6 tt.computeroutput { font-size: 130% } + + +/*============================================================================= + Author +=============================================================================*/ + + h3.author + { + font-size: 100% + } + +/*============================================================================= + Lists +=============================================================================*/ + + li + { + font-size: 10pt; + line-height: 1.3; + } + + /* Unordered lists */ + ul + { + text-align: left; + } + + /* Ordered lists */ + ol + { + text-align: left; + } + +/*============================================================================= + Links +=============================================================================*/ + + a + { + text-decoration: none; /* no underline */ + } + + a:hover + { + text-decoration: underline; + } + +/*============================================================================= + Spirit style navigation +=============================================================================*/ + + .spirit-nav + { + text-align: right; + } + + .spirit-nav a + { + color: white; + padding-left: 0.5em; + } + + .spirit-nav img + { + border-width: 0px; + } + +/*============================================================================= + Copyright footer +=============================================================================*/ + .copyright-footer + { + text-align: right; + font-size: 70%; + } + + .copyright-footer p + { + text-align: right; + font-size: 80%; + } + +/*============================================================================= + Table of contents +=============================================================================*/ + + .toc + { + margin: 1pc 4% 0pc 4%; + padding: 0.1pc 1pc 0.1pc 1pc; + font-size: 80%; + line-height: 1.15; + } + + .boost-toc + { + float: right; + padding: 0.5pc; + } + + /* Code on toc */ + .toc .computeroutput { font-size: 120% } + +/*============================================================================= + Tables +=============================================================================*/ + + .table-title, + div.table p.title + { + margin-left: 4%; + padding-right: 0.5em; + padding-left: 0.5em; + } + + .informaltable table, + .table table + { + width: 92%; + margin-left: 4%; + margin-right: 4%; + } + + div.informaltable table, + div.table table + { + padding: 4px; + } + + /* Table Cells */ + div.informaltable table tr td, + div.table table tr td + { + padding: 0.5em; + text-align: left; + font-size: 9pt; + } + + div.informaltable table tr th, + div.table table tr th + { + padding: 0.5em 0.5em 0.5em 0.5em; + border: 1pt solid white; + font-size: 80%; + } + + table.simplelist + { + width: auto !important; + margin: 0em !important; + padding: 0em !important; + border: none !important; + } + table.simplelist td + { + margin: 0em !important; + padding: 0em !important; + text-align: left !important; + font-size: 9pt !important; + border: none !important; + } + +/*============================================================================= + Blurbs +=============================================================================*/ + + div.note, + div.tip, + div.important, + div.caution, + div.warning, + p.blurb + { + font-size: 9pt; /* A little bit smaller than the main text */ + line-height: 1.2; + display: block; + margin: 1pc 4% 0pc 4%; + padding: 0.5pc 0.5pc 0.5pc 0.5pc; + } + + p.blurb img + { + padding: 1pt; + } + +/*============================================================================= + Variable Lists +=============================================================================*/ + + div.variablelist + { + margin: 1em 0; + } + + /* Make the terms in definition lists bold */ + div.variablelist dl dt, + span.term + { + font-weight: bold; + font-size: 10pt; + } + + div.variablelist table tbody tr td + { + text-align: left; + vertical-align: top; + padding: 0em 2em 0em 0em; + font-size: 10pt; + margin: 0em 0em 0.5em 0em; + line-height: 1; + } + + div.variablelist dl dt + { + margin-bottom: 0.2em; + } + + div.variablelist dl dd + { + margin: 0em 0em 0.5em 2em; + font-size: 10pt; + } + + div.variablelist table tbody tr td p, + div.variablelist dl dd p + { + margin: 0em 0em 0.5em 0em; + line-height: 1; + } + +/*============================================================================= + Misc +=============================================================================*/ + + /* Title of books and articles in bibliographies */ + span.title + { + font-style: italic; + } + + span.underline + { + text-decoration: underline; + } + + span.strikethrough + { + text-decoration: line-through; + } + + /* Copyright, Legal Notice */ + div div.legalnotice p + { + text-align: left + } + +/*============================================================================= + Colors +=============================================================================*/ + + @media screen + { + body { + background-color: #FFFFFF; + color: #000000; + } + + /* Links */ + a + { + color: #005a9c; + } + + a:visited + { + color: #9c5a9c; + } + + h1 a, h2 a, h3 a, h4 a, h5 a, h6 a, + h1 a:hover, h2 a:hover, h3 a:hover, h4 a:hover, h5 a:hover, h6 a:hover, + h1 a:visited, h2 a:visited, h3 a:visited, h4 a:visited, h5 a:visited, h6 a:visited + { + text-decoration: none; /* no underline */ + color: #000000; + } + + /* Syntax Highlighting */ + .keyword { color: #0000AA; } + .identifier { color: #000000; } + .special { color: #707070; } + .preprocessor { color: #402080; } + .char { color: teal; } + .comment { color: #800000; } + .string { color: teal; } + .number { color: teal; } + .white_bkd { background-color: #FFFFFF; } + .dk_grey_bkd { background-color: #999999; } + + /* Copyright, Legal Notice */ + .copyright + { + color: #666666; + font-size: small; + } + + div div.legalnotice p + { + color: #666666; + } + + /* Program listing */ + pre.synopsis + { + border: 1px solid #DCDCDC; + } + + .programlisting, + .screen + { + border: 1px solid #DCDCDC; + } + + td .programlisting, + td .screen + { + border: 0px solid #DCDCDC; + } + + /* Blurbs */ + div.note, + div.tip, + div.important, + div.caution, + div.warning, + p.blurb + { + border: 1px solid #DCDCDC; + } + + /* Table of contents */ + .toc + { + border: 1px solid #DCDCDC; + } + + /* Tables */ + div.informaltable table tr td, + div.table table tr td + { + border: 1px solid #DCDCDC; + } + + div.informaltable table tr th, + div.table table tr th + { + background-color: #F0F0F0; + border: 1px solid #DCDCDC; + } + + .copyright-footer + { + color: #8F8F8F; + } + + /* Misc */ + span.highlight + { + color: #00A000; + } + } + + @media print + { + /* Links */ + a + { + color: black; + } + + a:visited + { + color: black; + } + + .spirit-nav + { + display: none; + } + + /* Program listing */ + pre.synopsis + { + border: 1px solid gray; + } + + .programlisting, + .screen + { + border: 1px solid gray; + } + + td .programlisting, + td .screen + { + border: 0px solid #DCDCDC; + } + + /* Table of contents */ + .toc + { + border: 1px solid gray; + } + + .informaltable table, + .table table + { + border: 1px solid gray; + border-collapse: collapse; + } + + /* Tables */ + div.informaltable table tr td, + div.table table tr td + { + border: 1px solid gray; + } + + div.informaltable table tr th, + div.table table tr th + { + border: 1px solid gray; + } + + table.simplelist tr td + { + border: none !important; + } + + /* Misc */ + span.highlight + { + font-weight: bold; + } + } + +/*============================================================================= + Images +=============================================================================*/ + + span.inlinemediaobject img + { + vertical-align: middle; + } + +/*============================================================================== + Super and Subscript: style so that line spacing isn't effected, see + http://www.adobe.com/cfusion/communityengine/index.cfm?event=showdetails&productId=1&postId=5341 +==============================================================================*/ + +sup, +sub { + height: 0; + line-height: 1; + vertical-align: baseline; + _vertical-align: bottom; + position: relative; + +} + +sup { + bottom: 1ex; +} + +sub { + top: .5ex; +} + diff --git a/LifeGraphics/pugixml/docs/quickstart.html b/LifeGraphics/pugixml/docs/quickstart.html new file mode 100644 index 0000000..d39e552 --- /dev/null +++ b/LifeGraphics/pugixml/docs/quickstart.html @@ -0,0 +1,845 @@ + + + +pugixml 1.0 + + + + + +
+
+ + +
+ +

+ pugixml is a light-weight C++ XML + processing library. It consists of a DOM-like interface with rich traversal/modification + capabilities, an extremely fast XML parser which constructs the DOM tree + from an XML file/buffer, and an XPath 1.0 implementation for complex data-driven + tree queries. Full Unicode support is also available, with Unicode interface + variants and conversions between different Unicode encodings (which happen + automatically during parsing/saving). The library is extremely portable and + easy to integrate and use. pugixml is developed and maintained since 2006 + and has many users. All code is distributed under the MIT + license, making it completely free to use in both open-source and + proprietary applications. +

+

+ pugixml enables very fast, convenient and memory-efficient XML document processing. + However, since pugixml has a DOM parser, it can't process XML documents that + do not fit in memory; also the parser is a non-validating one, so if you + need DTD/Schema validation, the library is not for you. +

+

+ This is the quick start guide for pugixml, which purpose is to enable you + to start using the library quickly. Many important library features are either + not described at all or only mentioned briefly; for more complete information + you should read the complete manual. +

+
+ + + + + +
[Note]Note

+ No documentation is perfect, neither is this one. If you encounter a description + that is unclear, please file an issue as described in Feedback. Also if + you can spare the time for a full proof-reading, including spelling and + grammar, that would be great! Please send me an e-mail; + as a token of appreciation, your name will be included into the corresponding + section of the manual. +

+
+
+ +

+ pugixml is distributed in source form. You can download a source distribution + via one of the following links: +

+
http://pugixml.googlecode.com/files/pugixml-1.0.zip
+http://pugixml.googlecode.com/files/pugixml-1.0.tar.gz
+
+

+ The distribution contains library source, documentation (the guide you're + reading now and the manual) and some code examples. After downloading the + distribution, install pugixml by extracting all files from the compressed + archive. The files have different line endings depending on the archive format + - .zip archive has Windows line endings, .tar.gz archive has Unix line endings. + Otherwise the files in both archives are identical. +

+

+ The complete pugixml source consists of three files - one source file, pugixml.cpp, + and two header files, pugixml.hpp and pugiconfig.hpp. pugixml.hpp is the primary + header which you need to include in order to use pugixml classes/functions. + The rest of this guide assumes that pugixml.hpp is either in the current directory + or in one of include directories of your projects, so that #include "pugixml.hpp" + can find the header; however you can also use relative path (i.e. #include "../libs/pugixml/src/pugixml.hpp") + or include directory-relative path (i.e. #include + <xml/thirdparty/pugixml/src/pugixml.hpp>). +

+

+ The easiest way to build pugixml is to compile the source file, pugixml.cpp, + along with the existing library/executable. This process depends on the method + of building your application; for example, if you're using Microsoft Visual + Studio[1], + Apple Xcode, Code::Blocks or any other IDE, just add pugixml.cpp to one of + your projects. There are other building methods available, including building + pugixml as a standalone static/shared library; read + the manual for further information. +

+
+
+ +

+ pugixml stores XML data in DOM-like way: the entire XML document (both document + structure and element data) is stored in memory as a tree. The tree can be + loaded from character stream (file, string, C++ I/O stream), then traversed + via special API or XPath expressions. The whole tree is mutable: both node + structure and node/attribute data can be changed at any time. Finally, the + result of document transformations can be saved to a character stream (file, + C++ I/O stream or custom transport). +

+

+ The root of the tree is the document itself, which corresponds to C++ type + xml_document. Document has + one or more child nodes, which correspond to C++ type xml_node. + Nodes have different types; depending on a type, a node can have a collection + of child nodes, a collection of attributes, which correspond to C++ type + xml_attribute, and some additional + data (i.e. name). +

+

+ The most common node types are: +

+
    +
  • + Document node (node_document) + - this is the root of the tree, which consists of several child nodes. + This node corresponds to xml_document + class; note that xml_document + is a sub-class of xml_node, + so the entire node interface is also available. +
  • +
  • + Element/tag node (node_element) + - this is the most common type of node, which represents XML elements. + Element nodes have a name, a collection of attributes and a collection + of child nodes (both of which may be empty). The attribute is a simple + name/value pair. +
  • +
  • + Plain character data nodes (node_pcdata) + represent plain text in XML. PCDATA nodes have a value, but do not have + name or children/attributes. Note that plain character data is not a + part of the element node but instead has its own node; for example, an + element node can have several child PCDATA nodes. +
  • +
+

+ Despite the fact that there are several node types, there are only three + C++ types representing the tree (xml_document, + xml_node, xml_attribute); + some operations on xml_node + are only valid for certain node types. They are described below. +

+
+ + + + + +
[Note]Note

+ All pugixml classes and functions are located in pugi + namespace; you have to either use explicit name qualification (i.e. pugi::xml_node), or to gain access to relevant + symbols via using directive + (i.e. using pugi::xml_node; or using + namespace pugi;). +

+

+ xml_document is the owner + of the entire document structure; destroying the document destroys the whole + tree. The interface of xml_document + consists of loading functions, saving functions and the entire interface + of xml_node, which allows + for document inspection and/or modification. Note that while xml_document is a sub-class of xml_node, xml_node + is not a polymorphic type; the inheritance is present only to simplify usage. +

+

+ xml_node is the handle to + document node; it can point to any node in the document, including document + itself. There is a common interface for nodes of all types. Note that xml_node is only a handle to the actual + node, not the node itself - you can have several xml_node + handles pointing to the same underlying object. Destroying xml_node handle does not destroy the node + and does not remove it from the tree. +

+

+ There is a special value of xml_node + type, known as null node or empty node. It does not correspond to any node + in any document, and thus resembles null pointer. However, all operations + are defined on empty nodes; generally the operations don't do anything and + return empty nodes/attributes or empty strings as their result. This is useful + for chaining calls; i.e. you can get the grandparent of a node like so: + node.parent().parent(); + if a node is a null node or it does not have a parent, the first parent() + call returns null node; the second parent() call then also returns null node, so you + don't have to check for errors twice. You can test if a handle is null via + implicit boolean cast: if (node) { ... } + or if (!node) { ... }. +

+

+ xml_attribute is the handle + to an XML attribute; it has the same semantics as xml_node, + i.e. there can be several xml_attribute + handles pointing to the same underlying object and there is a special null + attribute value, which propagates to function results. +

+

+ There are two choices of interface and internal representation when configuring + pugixml: you can either choose the UTF-8 (also called char) interface or + UTF-16/32 (also called wchar_t) one. The choice is controlled via PUGIXML_WCHAR_MODE define; you can set + it via pugiconfig.hpp or via preprocessor options. All tree functions that + work with strings work with either C-style null terminated strings or STL + strings of the selected character type. Read + the manual for additional information on Unicode interface. +

+
+
+ +

+ pugixml provides several functions for loading XML data from various places + - files, C++ iostreams, memory buffers. All functions use an extremely fast + non-validating parser. This parser is not fully W3C conformant - it can load + any valid XML document, but does not perform some well-formedness checks. + While considerable effort is made to reject invalid XML documents, some validation + is not performed because of performance reasons. XML data is always converted + to internal character format before parsing. pugixml supports all popular + Unicode encodings (UTF-8, UTF-16 (big and little endian), UTF-32 (big and + little endian); UCS-2 is naturally supported since it's a strict subset of + UTF-16) and handles all encoding conversions automatically. +

+

+ The most common source of XML data is files; pugixml provides a separate + function for loading XML document from file. This function accepts file path + as its first argument, and also two optional arguments, which specify parsing + options and input data encoding, which are described in the manual. +

+

+ This is an example of loading XML document from file (samples/load_file.cpp): +

+

+ +

+
pugi::xml_document doc;
+
+pugi::xml_parse_result result = doc.load_file("tree.xml");
+
+std::cout << "Load result: " << result.description() << ", mesh name: " << doc.child("mesh").attribute("name").value() << std::endl;
+
+

+

+

+ load_file, as well as other + loading functions, destroys the existing document tree and then tries to + load the new tree from the specified file. The result of the operation is + returned in an xml_parse_result + object; this object contains the operation status, and the related information + (i.e. last successfully parsed position in the input file, if parsing fails). +

+

+ Parsing result object can be implicitly converted to bool; + if you do not want to handle parsing errors thoroughly, you can just check + the return value of load functions as if it was a bool: + if (doc.load_file("file.xml")) { ... + } else { ... }. + Otherwise you can use the status + member to get parsing status, or the description() member function to get the status in a + string form. +

+

+ This is an example of handling loading errors (samples/load_error_handling.cpp): +

+

+ +

+
pugi::xml_document doc;
+pugi::xml_parse_result result = doc.load(source);
+
+if (result)
+    std::cout << "XML [" << source << "] parsed without errors, attr value: [" << doc.child("node").attribute("attr").value() << "]\n\n";
+else
+{
+    std::cout << "XML [" << source << "] parsed with errors, attr value: [" << doc.child("node").attribute("attr").value() << "]\n";
+    std::cout << "Error description: " << result.description() << "\n";
+    std::cout << "Error offset: " << result.offset << " (error at [..." << (source + result.offset) << "]\n\n";
+}
+
+

+

+

+ Sometimes XML data should be loaded from some other source than file, i.e. + HTTP URL; also you may want to load XML data from file using non-standard + functions, i.e. to use your virtual file system facilities or to load XML + from gzip-compressed files. These scenarios either require loading document + from memory, in which case you should prepare a contiguous memory block with + all XML data and to pass it to one of buffer loading functions, or loading + document from C++ IOstream, in which case you should provide an object which + implements std::istream or std::wistream + interface. +

+

+ There are different functions for loading document from memory; they treat + the passed buffer as either an immutable one (load_buffer), + a mutable buffer which is owned by the caller (load_buffer_inplace), + or a mutable buffer which ownership belongs to pugixml (load_buffer_inplace_own). + There is also a simple helper function, xml_document::load, + for cases when you want to load the XML document from null-terminated character + string. +

+

+ This is an example of loading XML document from memory using one of these + functions (samples/load_memory.cpp); + read the sample code for more examples: +

+

+ +

+
const char source[] = "<mesh name='sphere'><bounds>0 0 1 1</bounds></mesh>";
+size_t size = sizeof(source);
+
+

+

+

+ +

+
// You can use load_buffer_inplace to load document from mutable memory block; the block's lifetime must exceed that of document
+char* buffer = new char[size];
+memcpy(buffer, source, size);
+
+// The block can be allocated by any method; the block is modified during parsing
+pugi::xml_parse_result result = doc.load_buffer_inplace(buffer, size);
+
+// You have to destroy the block yourself after the document is no longer used
+delete[] buffer;
+
+

+

+

+ This is a simple example of loading XML document from file using streams + (samples/load_stream.cpp); read + the sample code for more complex examples involving wide streams and locales: +

+

+ +

+
std::ifstream stream("weekly-utf-8.xml");
+pugi::xml_parse_result result = doc.load(stream);
+
+

+

+
+
+ +

+ pugixml features an extensive interface for getting various types of data + from the document and for traversing the document. You can use various accessors + to get node/attribute data, you can traverse the child node/attribute lists + via accessors or iterators, you can do depth-first traversals with xml_tree_walker objects, and you can use + XPath for complex data-driven queries. +

+

+ You can get node or attribute name via name() accessor, and value via value() accessor. Note that both functions never + return null pointers - they either return a string with the relevant content, + or an empty string if name/value is absent or if the handle is null. Also + there are two notable things for reading values: +

+
    +
  • + It is common to store data as text contents of some node - i.e. <node><description>This + is a + node</description></node>. + In this case, <description> node does not have a value, but instead + has a child of type node_pcdata + with value "This is a node". + pugixml provides child_value() helper functions to parse such data. +
  • +
  • + In many cases attribute values have types that are not strings - i.e. + an attribute may always contain values that should be treated as integers, + despite the fact that they are represented as strings in XML. pugixml + provides several accessors that convert attribute value to some other + type. +
  • +
+

+ This is an example of using these functions (samples/traverse_base.cpp): +

+

+ +

+
for (pugi::xml_node tool = tools.child("Tool"); tool; tool = tool.next_sibling("Tool"))
+{
+    std::cout << "Tool " << tool.attribute("Filename").value();
+    std::cout << ": AllowRemote " << tool.attribute("AllowRemote").as_bool();
+    std::cout << ", Timeout " << tool.attribute("Timeout").as_int();
+    std::cout << ", Description '" << tool.child_value("Description") << "'\n";
+}
+
+

+

+

+ Since a lot of document traversal consists of finding the node/attribute + with the correct name, there are special functions for that purpose. For + example, child("Tool") + returns the first node which has the name "Tool", + or null handle if there is no such node. This is an example of using such + functions (samples/traverse_base.cpp): +

+

+ +

+
std::cout << "Tool for *.dae generation: " << tools.find_child_by_attribute("Tool", "OutputFileMasks", "*.dae").attribute("Filename").value() << "\n";
+
+for (pugi::xml_node tool = tools.child("Tool"); tool; tool = tool.next_sibling("Tool"))
+{
+    std::cout << "Tool " << tool.attribute("Filename").value() << "\n";
+}
+
+

+

+

+ Child node lists and attribute lists are simply double-linked lists; while + you can use previous_sibling/next_sibling and other such functions for + iteration, pugixml additionally provides node and attribute iterators, so + that you can treat nodes as containers of other nodes or attributes. All + iterators are bidirectional and support all usual iterator operations. The + iterators are invalidated if the node/attribute objects they're pointing + to are removed from the tree; adding nodes/attributes does not invalidate + any iterators. +

+

+ Here is an example of using iterators for document traversal (samples/traverse_iter.cpp): +

+

+ +

+
for (pugi::xml_node_iterator it = tools.begin(); it != tools.end(); ++it)
+{
+    std::cout << "Tool:";
+
+    for (pugi::xml_attribute_iterator ait = it->attributes_begin(); ait != it->attributes_end(); ++ait)
+    {
+        std::cout << " " << ait->name() << "=" << ait->value();
+    }
+
+    std::cout << std::endl;
+}
+
+

+

+

+ The methods described above allow traversal of immediate children of some + node; if you want to do a deep tree traversal, you'll have to do it via a + recursive function or some equivalent method. However, pugixml provides a + helper for depth-first traversal of a subtree. In order to use it, you have + to implement xml_tree_walker + interface and to call traverse + function. +

+

+ This is an example of traversing tree hierarchy with xml_tree_walker (samples/traverse_walker.cpp): +

+

+ +

+
struct simple_walker: pugi::xml_tree_walker
+{
+    virtual bool for_each(pugi::xml_node& node)
+    {
+        for (int i = 0; i < depth(); ++i) std::cout << "  "; // indentation
+
+        std::cout << node_types[node.type()] << ": name='" << node.name() << "', value='" << node.value() << "'\n";
+
+        return true; // continue traversal
+    }
+};
+
+

+

+

+ +

+
simple_walker walker;
+doc.traverse(walker);
+
+

+

+

+ Finally, for complex queries often a higher-level DSL is needed. pugixml + provides an implementation of XPath 1.0 language for such queries. The complete + description of XPath usage can be found in the manual, but here are some + examples: +

+

+ +

+
pugi::xpath_node_set tools = doc.select_nodes("/Profile/Tools/Tool[@AllowRemote='true' and @DeriveCaptionFrom='lastparam']");
+
+std::cout << "Tools:";
+
+for (pugi::xpath_node_set::const_iterator it = tools.begin(); it != tools.end(); ++it)
+{
+    pugi::xpath_node node = *it;
+    std::cout << " " << node.node().attribute("Filename").value();
+}
+
+pugi::xpath_node build_tool = doc.select_single_node("//Tool[contains(Description, 'build system')]");
+
+std::cout << "\nBuild tool: " << build_tool.node().attribute("Filename").value() << "\n";
+
+

+

+
+ + + + + +
[Caution]Caution

+ XPath functions throw xpath_exception + objects on error; the sample above does not catch these exceptions. +

+
+
+ +

+ The document in pugixml is fully mutable: you can completely change the document + structure and modify the data of nodes/attributes. All functions take care + of memory management and structural integrity themselves, so they always + result in structurally valid tree - however, it is possible to create an + invalid XML tree (for example, by adding two attributes with the same name + or by setting attribute/node name to empty/invalid string). Tree modification + is optimized for performance and for memory consumption, so if you have enough + memory you can create documents from scratch with pugixml and later save + them to file/stream instead of relying on error-prone manual text writing + and without too much overhead. +

+

+ All member functions that change node/attribute data or structure are non-constant + and thus can not be called on constant handles. However, you can easily convert + constant handle to non-constant one by simple assignment: void + foo(const pugi::xml_node& n) { pugi::xml_node nc = n; }, so const-correctness + here mainly provides additional documentation. +

+

+ As discussed before, nodes can have name and value, both of which are strings. + Depending on node type, name or value may be absent. You can use set_name and set_value + member functions to set them. Similar functions are available for attributes; + however, the set_value function + is overloaded for some other types except strings, like floating-point numbers. + Also, attribute value can be set using an assignment operator. This is an + example of setting node/attribute name and value (samples/modify_base.cpp): +

+

+ +

+
pugi::xml_node node = doc.child("node");
+
+// change node name
+std::cout << node.set_name("notnode");
+std::cout << ", new node name: " << node.name() << std::endl;
+
+// change comment text
+std::cout << doc.last_child().set_value("useless comment");
+std::cout << ", new comment text: " << doc.last_child().value() << std::endl;
+
+// we can't change value of the element or name of the comment
+std::cout << node.set_value("1") << ", " << doc.last_child().set_name("2") << std::endl;
+
+

+

+

+ +

+
pugi::xml_attribute attr = node.attribute("id");
+
+// change attribute name/value
+std::cout << attr.set_name("key") << ", " << attr.set_value("345");
+std::cout << ", new attribute: " << attr.name() << "=" << attr.value() << std::endl;
+
+// we can use numbers or booleans
+attr.set_value(1.234);
+std::cout << "new attribute value: " << attr.value() << std::endl;
+
+// we can also use assignment operators for more concise code
+attr = true;
+std::cout << "final attribute value: " << attr.value() << std::endl;
+
+

+

+

+ Nodes and attributes do not exist without a document tree, so you can't create + them without adding them to some document. A node or attribute can be created + at the end of node/attribute list or before/after some other node. All insertion + functions return the handle to newly created object on success, and null + handle on failure. Even if the operation fails (for example, if you're trying + to add a child node to PCDATA node), the document remains in consistent state, + but the requested node/attribute is not added. +

+
+ + + + + +
[Caution]Caution

+ attribute() and child() functions do not add attributes or nodes to the + tree, so code like node.attribute("id") = 123; will not do anything if node does not have an attribute with + name "id". Make sure + you're operating with existing attributes/nodes by adding them if necessary. +

+

+ This is an example of adding new attributes/nodes to the document (samples/modify_add.cpp): +

+

+ +

+
// add node with some name
+pugi::xml_node node = doc.append_child("node");
+
+// add description node with text child
+pugi::xml_node descr = node.append_child("description");
+descr.append_child(pugi::node_pcdata).set_value("Simple node");
+
+// add param node before the description
+pugi::xml_node param = node.insert_child_before("param", descr);
+
+// add attributes to param node
+param.append_attribute("name") = "version";
+param.append_attribute("value") = 1.1;
+param.insert_attribute_after("type", param.attribute("name")) = "float";
+
+

+

+

+ If you do not want your document to contain some node or attribute, you can + remove it with remove_attribute + and remove_child functions. + Removing the attribute or node invalidates all handles to the same underlying + object, and also invalidates all iterators pointing to the same object. Removing + node also invalidates all past-the-end iterators to its attribute or child + node list. Be careful to ensure that all such handles and iterators either + do not exist or are not used after the attribute/node is removed. +

+

+ This is an example of removing attributes/nodes from the document (samples/modify_remove.cpp): +

+

+ +

+
// remove description node with the whole subtree
+pugi::xml_node node = doc.child("node");
+node.remove_child("description");
+
+// remove id attribute
+pugi::xml_node param = node.child("param");
+param.remove_attribute("value");
+
+// we can also remove nodes/attributes by handles
+pugi::xml_attribute id = param.attribute("name");
+param.remove_attribute(id);
+
+

+

+
+
+ +

+ Often after creating a new document or loading the existing one and processing + it, it is necessary to save the result back to file. Also it is occasionally + useful to output the whole document or a subtree to some stream; use cases + include debug printing, serialization via network or other text-oriented + medium, etc. pugixml provides several functions to output any subtree of + the document to a file, stream or another generic transport interface; these + functions allow to customize the output format, and also perform necessary + encoding conversions. +

+

+ Before writing to the destination the node/attribute data is properly formatted + according to the node type; all special XML symbols, such as < and &, + are properly escaped. In order to guard against forgotten node/attribute + names, empty node/attribute names are printed as ":anonymous". + For well-formed output, make sure all node and attribute names are set to + meaningful values. +

+

+ If you want to save the whole document to a file, you can use the save_file function, which returns true on success. This is a simple example + of saving XML document to file (samples/save_file.cpp): +

+

+ +

+
// save document to file
+std::cout << "Saving result: " << doc.save_file("save_file_output.xml") << std::endl;
+
+

+

+

+ To enhance interoperability pugixml provides functions for saving document + to any object which implements C++ std::ostream + interface. This allows you to save documents to any standard C++ stream (i.e. + file stream) or any third-party compliant implementation (i.e. Boost Iostreams). + Most notably, this allows for easy debug output, since you can use std::cout + stream as saving target. There are two functions, one works with narrow character + streams, another handles wide character ones. +

+

+ This is a simple example of saving XML document to standard output (samples/save_stream.cpp): +

+

+ +

+
// save document to standard output
+std::cout << "Document:\n";
+doc.save(std::cout);
+
+

+

+

+ All of the above saving functions are implemented in terms of writer interface. + This is a simple interface with a single function, which is called several + times during output process with chunks of document data as input. In order + to output the document via some custom transport, for example sockets, you + should create an object which implements xml_writer_file + interface and pass it to xml_document::save + function. +

+

+ This is a simple example of custom writer for saving document data to STL + string (samples/save_custom_writer.cpp); + read the sample code for more complex examples: +

+

+ +

+
struct xml_string_writer: pugi::xml_writer
+{
+    std::string result;
+
+    virtual void write(const void* data, size_t size)
+    {
+        result += std::string(static_cast<const char*>(data), size);
+    }
+};
+
+

+

+

+ While the previously described functions save the whole document to the destination, + it is easy to save a single subtree. Instead of calling xml_document::save, + just call xml_node::print function on the target node. You + can save node contents to C++ IOstream object or custom writer in this way. + Saving a subtree slightly differs from saving the whole document; read the manual for + more information. +

+
+
+ +

+ If you believe you've found a bug in pugixml, please file an issue via issue submission form. + Be sure to include the relevant information so that the bug can be reproduced: + the version of pugixml, compiler version and target architecture, the code + that uses pugixml and exhibits the bug, etc. Feature requests and contributions + can be filed as issues, too. +

+

+ If filing an issue is not possible due to privacy or other concerns, you + can contact pugixml author by e-mail directly: arseny.kapoulkine@gmail.com. +

+
+
+ +

+ The pugixml library is distributed under the MIT license: +

+
+

+ Copyright (c) 2006-2010 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. +

+
+

+ This means that you can freely use pugixml in your applications, both open-source + and proprietary. If you use pugixml in a product, it is sufficient to add + an acknowledgment like this to the product distribution: +

+

+ This software is based on pugixml library (http://pugixml.org).
+pugixml + is Copyright (C) 2006-2010 Arseny Kapoulkine. +

+
+
+
+

+

[1] All trademarks used are properties of their respective owners.

+
+
+ + + +

Last revised: October 31, 2010 at 07:44:52 GMT

+ + diff --git a/LifeGraphics/pugixml/docs/samples/character.xml b/LifeGraphics/pugixml/docs/samples/character.xml new file mode 100644 index 0000000..078e0bf --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/character.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/LifeGraphics/pugixml/docs/samples/custom_memory_management.cpp b/LifeGraphics/pugixml/docs/samples/custom_memory_management.cpp new file mode 100644 index 0000000..92ccb71 --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/custom_memory_management.cpp @@ -0,0 +1,27 @@ +#include "pugixml.hpp" + +#include + +//[code_custom_memory_management_decl +void* custom_allocate(size_t size) +{ + return new (std::nothrow) char[size]; +} + +void custom_deallocate(void* ptr) +{ + delete[] static_cast(ptr); +} +//] + +int main() +{ +//[code_custom_memory_management_call + pugi::set_memory_management_functions(custom_allocate, custom_deallocate); +//] + + pugi::xml_document doc; + doc.load(""); +} + +// vim:et diff --git a/LifeGraphics/pugixml/docs/samples/include.cpp b/LifeGraphics/pugixml/docs/samples/include.cpp new file mode 100644 index 0000000..fa615a4 --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/include.cpp @@ -0,0 +1,64 @@ +#include "pugixml.hpp" + +#include +#include + +//[code_include +bool load_preprocess(pugi::xml_document& doc, const char* path); + +bool preprocess(pugi::xml_node node) +{ + for (pugi::xml_node child = node.first_child(); child; ) + { + if (child.type() == pugi::node_pi && strcmp(child.name(), "include") == 0) + { + pugi::xml_node include = child; + + // load new preprocessed document (note: ideally this should handle relative paths) + const char* path = include.value(); + + pugi::xml_document doc; + if (!load_preprocess(doc, path)) return false; + + // insert the comment marker above include directive + node.insert_child_before(pugi::node_comment, include).set_value(path); + + // copy the document above the include directive (this retains the original order!) + for (pugi::xml_node ic = doc.first_child(); ic; ic = ic.next_sibling()) + { + node.insert_copy_before(ic, include); + } + + // remove the include node and move to the next child + child = child.next_sibling(); + + node.remove_child(include); + } + else + { + if (!preprocess(child)) return false; + + child = child.next_sibling(); + } + } + + return true; +} + +bool load_preprocess(pugi::xml_document& doc, const char* path) +{ + pugi::xml_parse_result result = doc.load_file(path, pugi::parse_default | pugi::parse_pi); // for + + return result ? preprocess(doc) : false; +} +//] + +int main() +{ + pugi::xml_document doc; + if (!load_preprocess(doc, "character.xml")) return -1; + + doc.print(std::cout); +} + +// vim:et diff --git a/LifeGraphics/pugixml/docs/samples/load_error_handling.cpp b/LifeGraphics/pugixml/docs/samples/load_error_handling.cpp new file mode 100644 index 0000000..18dd331 --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/load_error_handling.cpp @@ -0,0 +1,31 @@ +#include "pugixml.hpp" + +#include + +void check_xml(const char* source) +{ +//[code_load_error_handling + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load(source); + + if (result) + std::cout << "XML [" << source << "] parsed without errors, attr value: [" << doc.child("node").attribute("attr").value() << "]\n\n"; + else + { + std::cout << "XML [" << source << "] parsed with errors, attr value: [" << doc.child("node").attribute("attr").value() << "]\n"; + std::cout << "Error description: " << result.description() << "\n"; + std::cout << "Error offset: " << result.offset << " (error at [..." << (source + result.offset) << "]\n\n"; + } +//] +} + +int main() +{ + check_xml("text"); + check_xml("text"); + check_xml("text"); + check_xml("<#tag />"); +} + +// vim:et diff --git a/LifeGraphics/pugixml/docs/samples/load_file.cpp b/LifeGraphics/pugixml/docs/samples/load_file.cpp new file mode 100644 index 0000000..f7b06c9 --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/load_file.cpp @@ -0,0 +1,16 @@ +#include "pugixml.hpp" + +#include + +int main() +{ +//[code_load_file + pugi::xml_document doc; + + pugi::xml_parse_result result = doc.load_file("tree.xml"); + + std::cout << "Load result: " << result.description() << ", mesh name: " << doc.child("mesh").attribute("name").value() << std::endl; +//] +} + +// vim:et diff --git a/LifeGraphics/pugixml/docs/samples/load_memory.cpp b/LifeGraphics/pugixml/docs/samples/load_memory.cpp new file mode 100644 index 0000000..365fb64 --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/load_memory.cpp @@ -0,0 +1,64 @@ +#include "pugixml.hpp" + +#include + +int main() +{ +//[code_load_memory_decl + const char source[] = "0 0 1 1"; + size_t size = sizeof(source); +//] + + pugi::xml_document doc; + + { + //[code_load_memory_buffer + // You can use load_buffer to load document from immutable memory block: + pugi::xml_parse_result result = doc.load_buffer(source, size); + //] + + std::cout << "Load result: " << result.description() << ", mesh name: " << doc.child("mesh").attribute("name").value() << std::endl; + } + + { + //[code_load_memory_buffer_inplace + // You can use load_buffer_inplace to load document from mutable memory block; the block's lifetime must exceed that of document + char* buffer = new char[size]; + memcpy(buffer, source, size); + + // The block can be allocated by any method; the block is modified during parsing + pugi::xml_parse_result result = doc.load_buffer_inplace(buffer, size); + + //<- + std::cout << "Load result: " << result.description() << ", mesh name: " << doc.child("mesh").attribute("name").value() << std::endl; + //-> + // You have to destroy the block yourself after the document is no longer used + delete[] buffer; + //] + } + + { + //[code_load_memory_buffer_inplace_own + // You can use load_buffer_inplace_own to load document from mutable memory block and to pass the ownership of this block + // The block has to be allocated via pugixml allocation function - using i.e. operator new here is incorrect + char* buffer = static_cast(pugi::get_memory_allocation_function()(size)); + memcpy(buffer, source, size); + + // The block will be deleted by the document + pugi::xml_parse_result result = doc.load_buffer_inplace_own(buffer, size); + //] + + std::cout << "Load result: " << result.description() << ", mesh name: " << doc.child("mesh").attribute("name").value() << std::endl; + } + + { + //[code_load_memory_string + // You can use load to load document from null-terminated strings, for example literals: + pugi::xml_parse_result result = doc.load("0 0 1 1"); + //] + + std::cout << "Load result: " << result.description() << ", mesh name: " << doc.child("mesh").attribute("name").value() << std::endl; + } +} + +// vim:et diff --git a/LifeGraphics/pugixml/docs/samples/load_options.cpp b/LifeGraphics/pugixml/docs/samples/load_options.cpp new file mode 100644 index 0000000..04b4b46 --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/load_options.cpp @@ -0,0 +1,30 @@ +#include "pugixml.hpp" + +#include + +int main() +{ + pugi::xml_document doc; + +//[code_load_options + const char* source = "<"; + + // Parsing with default options; note that comment node is not added to the tree, and entity reference < is expanded + doc.load(source); + std::cout << "First node value: [" << doc.first_child().value() << "], node child value: [" << doc.child_value("node") << "]\n"; + + // Parsing with additional parse_comments option; comment node is now added to the tree + doc.load(source, pugi::parse_default | pugi::parse_comments); + std::cout << "First node value: [" << doc.first_child().value() << "], node child value: [" << doc.child_value("node") << "]\n"; + + // Parsing with additional parse_comments option and without the (default) parse_escapes option; < is not expanded + doc.load(source, (pugi::parse_default | pugi::parse_comments) & ~pugi::parse_escapes); + std::cout << "First node value: [" << doc.first_child().value() << "], node child value: [" << doc.child_value("node") << "]\n"; + + // Parsing with minimal option mask; comment node is not added to the tree, and < is not expanded + doc.load(source, pugi::parse_minimal); + std::cout << "First node value: [" << doc.first_child().value() << "], node child value: [" << doc.child_value("node") << "]\n"; +//] +} + +// vim:et diff --git a/LifeGraphics/pugixml/docs/samples/load_stream.cpp b/LifeGraphics/pugixml/docs/samples/load_stream.cpp new file mode 100644 index 0000000..05cfb7f --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/load_stream.cpp @@ -0,0 +1,97 @@ +#include "pugixml.hpp" + +#include +#include +#include + +void print_doc(const char* message, const pugi::xml_document& doc, const pugi::xml_parse_result& result) +{ + std::cout + << message + << "\t: load result '" << result.description() << "'" + << ", first character of root name: U+" << std::hex << std::uppercase << std::setw(4) << std::setfill('0') << pugi::as_wide(doc.first_child().name())[0] + << ", year: " << doc.first_child().first_child().first_child().child_value() + << std::endl; +} + +bool try_imbue(std::wistream& stream, const char* name) +{ + try + { + stream.imbue(std::locale(name)); + + return true; + } + catch (const std::exception&) + { + return false; + } +} + +int main() +{ + pugi::xml_document doc; + + { + //[code_load_stream + std::ifstream stream("weekly-utf-8.xml"); + pugi::xml_parse_result result = doc.load(stream); + //] + + // first character of root name: U+9031, year: 1997 + print_doc("UTF8 file from narrow stream", doc, result); + } + + { + std::ifstream stream("weekly-utf-16.xml"); + pugi::xml_parse_result result = doc.load(stream); + + // first character of root name: U+9031, year: 1997 + print_doc("UTF16 file from narrow stream", doc, result); + } + + { + // Since wide streams are treated as UTF-16/32 ones, you can't load the UTF-8 file from a wide stream + // directly if you have localized characters; you'll have to provide a UTF8 locale (there is no + // standard one; you can use utf8_codecvt_facet from Boost or codecvt_utf8 from C++0x) + std::wifstream stream("weekly-utf-8.xml"); + + if (try_imbue(stream, "en_US.UTF-8")) // try Linux encoding + { + pugi::xml_parse_result result = doc.load(stream); + + // first character of root name: U+00E9, year: 1997 + print_doc("UTF8 file from wide stream", doc, result); + } + else + { + std::cout << "UTF-8 locale is not available\n"; + } + } + + { + // Since wide streams are treated as UTF-16/32 ones, you can't load the UTF-16 file from a wide stream without + // using custom codecvt; you can use codecvt_utf16 from C++0x + } + + { + // Since encoding names are non-standard, you can't load the Shift-JIS (or any other non-ASCII) file + // from a wide stream portably + std::wifstream stream("weekly-shift_jis.xml"); + + if (try_imbue(stream, ".932") || // try Microsoft encoding + try_imbue(stream, "ja_JP.SJIS")) // try Linux encoding; run "localedef -i ja_JP -c -f SHIFT_JIS /usr/lib/locale/ja_JP.SJIS" to get it + { + pugi::xml_parse_result result = doc.load(stream); + + // first character of root name: U+9031, year: 1997 + print_doc("Shift-JIS file from wide stream", doc, result); + } + else + { + std::cout << "Shift-JIS locale is not available\n"; + } + } +} + +// vim:et diff --git a/LifeGraphics/pugixml/docs/samples/modify_add.cpp b/LifeGraphics/pugixml/docs/samples/modify_add.cpp new file mode 100644 index 0000000..04ab445 --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/modify_add.cpp @@ -0,0 +1,29 @@ +#include "pugixml.hpp" + +#include + +int main() +{ + pugi::xml_document doc; + + //[code_modify_add + // add node with some name + pugi::xml_node node = doc.append_child("node"); + + // add description node with text child + pugi::xml_node descr = node.append_child("description"); + descr.append_child(pugi::node_pcdata).set_value("Simple node"); + + // add param node before the description + pugi::xml_node param = node.insert_child_before("param", descr); + + // add attributes to param node + param.append_attribute("name") = "version"; + param.append_attribute("value") = 1.1; + param.insert_attribute_after("type", param.attribute("name")) = "float"; + //] + + doc.print(std::cout); +} + +// vim:et diff --git a/LifeGraphics/pugixml/docs/samples/modify_base.cpp b/LifeGraphics/pugixml/docs/samples/modify_base.cpp new file mode 100644 index 0000000..7d0959a --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/modify_base.cpp @@ -0,0 +1,43 @@ +#include "pugixml.hpp" + +#include +#include + +int main() +{ + pugi::xml_document doc; + if (!doc.load("text", pugi::parse_default | pugi::parse_comments)) return -1; + + //[code_modify_base_node + pugi::xml_node node = doc.child("node"); + + // change node name + std::cout << node.set_name("notnode"); + std::cout << ", new node name: " << node.name() << std::endl; + + // change comment text + std::cout << doc.last_child().set_value("useless comment"); + std::cout << ", new comment text: " << doc.last_child().value() << std::endl; + + // we can't change value of the element or name of the comment + std::cout << node.set_value("1") << ", " << doc.last_child().set_name("2") << std::endl; + //] + + //[code_modify_base_attr + pugi::xml_attribute attr = node.attribute("id"); + + // change attribute name/value + std::cout << attr.set_name("key") << ", " << attr.set_value("345"); + std::cout << ", new attribute: " << attr.name() << "=" << attr.value() << std::endl; + + // we can use numbers or booleans + attr.set_value(1.234); + std::cout << "new attribute value: " << attr.value() << std::endl; + + // we can also use assignment operators for more concise code + attr = true; + std::cout << "final attribute value: " << attr.value() << std::endl; + //] +} + +// vim:et diff --git a/LifeGraphics/pugixml/docs/samples/modify_remove.cpp b/LifeGraphics/pugixml/docs/samples/modify_remove.cpp new file mode 100644 index 0000000..28c2f6b --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/modify_remove.cpp @@ -0,0 +1,27 @@ +#include "pugixml.hpp" + +#include + +int main() +{ + pugi::xml_document doc; + if (!doc.load("Simple node")) return -1; + + //[code_modify_remove + // remove description node with the whole subtree + pugi::xml_node node = doc.child("node"); + node.remove_child("description"); + + // remove id attribute + pugi::xml_node param = node.child("param"); + param.remove_attribute("value"); + + // we can also remove nodes/attributes by handles + pugi::xml_attribute id = param.attribute("name"); + param.remove_attribute(id); + //] + + doc.print(std::cout); +} + +// vim:et diff --git a/LifeGraphics/pugixml/docs/samples/save_custom_writer.cpp b/LifeGraphics/pugixml/docs/samples/save_custom_writer.cpp new file mode 100644 index 0000000..defcb33 --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/save_custom_writer.cpp @@ -0,0 +1,116 @@ +#include "pugixml.hpp" + +#include + +#include + +//[code_save_custom_writer +struct xml_string_writer: pugi::xml_writer +{ + std::string result; + + virtual void write(const void* data, size_t size) + { + result += std::string(static_cast(data), size); + } +}; +//] + +struct xml_memory_writer: pugi::xml_writer +{ + char* buffer; + size_t capacity; + + size_t result; + + xml_memory_writer(): buffer(0), capacity(0), result(0) + { + } + + xml_memory_writer(char* buffer, size_t capacity): buffer(buffer), capacity(capacity), result(0) + { + } + + size_t written_size() const + { + return result < capacity ? result : capacity; + } + + virtual void write(const void* data, size_t size) + { + if (result < capacity) + { + size_t chunk = (capacity - result < size) ? capacity - result : size; + + memcpy(buffer + result, data, chunk); + } + + result += size; + } +}; + +std::string node_to_string(pugi::xml_node node) +{ + xml_string_writer writer; + node.print(writer); + + return writer.result; +} + +char* node_to_buffer(pugi::xml_node node, char* buffer, size_t size) +{ + if (size == 0) return buffer; + + // leave one character for null terminator + xml_memory_writer writer(buffer, size - 1); + node.print(writer); + + // null terminate + buffer[writer.written_size()] = 0; + + return buffer; +} + +char* node_to_buffer_heap(pugi::xml_node node) +{ + // first pass: get required memory size + xml_memory_writer counter; + node.print(counter); + + // allocate necessary size (+1 for null termination) + char* buffer = new char[counter.result + 1]; + + // second pass: actual printing + xml_memory_writer writer(buffer, counter.result); + node.print(writer); + + // null terminate + buffer[writer.written_size()] = 0; + + return buffer; +} + +int main() +{ + // get a test document + pugi::xml_document doc; + doc.load("hey"); + + // get contents as std::string (single pass) + printf("contents: [%s]\n", node_to_string(doc).c_str()); + + // get contents into fixed-size buffer (single pass) + char large_buf[128]; + printf("contents: [%s]\n", node_to_buffer(doc, large_buf, sizeof(large_buf))); + + // get contents into fixed-size buffer (single pass, shows truncating behavior) + char small_buf[22]; + printf("contents: [%s]\n", node_to_buffer(doc, small_buf, sizeof(small_buf))); + + // get contents into heap-allocated buffer (two passes) + char* heap_buf = node_to_buffer_heap(doc); + printf("contents: [%s]\n", heap_buf); + delete[] heap_buf; +} + +// vim:et diff --git a/LifeGraphics/pugixml/docs/samples/save_file.cpp b/LifeGraphics/pugixml/docs/samples/save_file.cpp new file mode 100644 index 0000000..30c1aa1 --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/save_file.cpp @@ -0,0 +1,17 @@ +#include "pugixml.hpp" + +#include + +int main() +{ + // get a test document + pugi::xml_document doc; + doc.load("hey"); + + //[code_save_file + // save document to file + std::cout << "Saving result: " << doc.save_file("save_file_output.xml") << std::endl; + //] +} + +// vim:et diff --git a/LifeGraphics/pugixml/docs/samples/save_file_output.xml b/LifeGraphics/pugixml/docs/samples/save_file_output.xml new file mode 100644 index 0000000..c8d1b44 --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/save_file_output.xml @@ -0,0 +1,2 @@ + +hey diff --git a/LifeGraphics/pugixml/docs/samples/save_options.cpp b/LifeGraphics/pugixml/docs/samples/save_options.cpp new file mode 100644 index 0000000..6a49f66 --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/save_options.cpp @@ -0,0 +1,48 @@ +#include "pugixml.hpp" + +#include + +int main() +{ + //[code_save_options + // get a test document + pugi::xml_document doc; + doc.load("hey"); + + // default options; prints + // + // + // hey + // + doc.save(std::cout); + std::cout << std::endl; + + // default options with custom indentation string; prints + // + // + // --hey + // + doc.save(std::cout, "--"); + std::cout << std::endl; + + // default options without indentation; prints + // + // + // hey + // + doc.save(std::cout, "\t", pugi::format_default & ~pugi::format_indent); // can also pass "" instead of indentation string for the same effect + std::cout << std::endl; + + // raw output; prints + // hey + doc.save(std::cout, "\t", pugi::format_raw); + std::cout << std::endl << std::endl; + + // raw output without declaration; prints + // hey + doc.save(std::cout, "\t", pugi::format_raw | pugi::format_no_declaration); + std::cout << std::endl; + //] +} + +// vim:et diff --git a/LifeGraphics/pugixml/docs/samples/save_stream.cpp b/LifeGraphics/pugixml/docs/samples/save_stream.cpp new file mode 100644 index 0000000..d01965d --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/save_stream.cpp @@ -0,0 +1,18 @@ +#include "pugixml.hpp" + +#include + +int main() +{ + // get a test document + pugi::xml_document doc; + doc.load("hey"); + + //[code_save_stream + // save document to standard output + std::cout << "Document:\n"; + doc.save(std::cout); + //] +} + +// vim:et diff --git a/LifeGraphics/pugixml/docs/samples/save_subtree.cpp b/LifeGraphics/pugixml/docs/samples/save_subtree.cpp new file mode 100644 index 0000000..0091b3d --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/save_subtree.cpp @@ -0,0 +1,26 @@ +#include "pugixml.hpp" + +#include + +int main() +{ + //[code_save_subtree + // get a test document + pugi::xml_document doc; + doc.load("hey"); + + // print document to standard output (prints hey) + doc.save(std::cout, "", pugi::format_raw); + std::cout << std::endl; + + // print document to standard output as a regular node (prints hey) + doc.print(std::cout, "", pugi::format_raw); + std::cout << std::endl; + + // print a subtree to standard output (prints hey) + doc.child("foo").child("call").print(std::cout, "", pugi::format_raw); + std::cout << std::endl; + //] +} + +// vim:et diff --git a/LifeGraphics/pugixml/docs/samples/transitions.xml b/LifeGraphics/pugixml/docs/samples/transitions.xml new file mode 100644 index 0000000..a195ef8 --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/transitions.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/LifeGraphics/pugixml/docs/samples/traverse_base.cpp b/LifeGraphics/pugixml/docs/samples/traverse_base.cpp new file mode 100644 index 0000000..d59c8b0 --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/traverse_base.cpp @@ -0,0 +1,51 @@ +#include "pugixml.hpp" + +#include +#include + +int main() +{ + pugi::xml_document doc; + if (!doc.load_file("xgconsole.xml")) return -1; + + pugi::xml_node tools = doc.child("Profile").child("Tools"); + + //[code_traverse_base_basic + for (pugi::xml_node tool = tools.first_child(); tool; tool = tool.next_sibling()) + { + std::cout << "Tool:"; + + for (pugi::xml_attribute attr = tool.first_attribute(); attr; attr = attr.next_attribute()) + { + std::cout << " " << attr.name() << "=" << attr.value(); + } + + std::cout << std::endl; + } + //] + + std::cout << std::endl; + + //[code_traverse_base_data + for (pugi::xml_node tool = tools.child("Tool"); tool; tool = tool.next_sibling("Tool")) + { + std::cout << "Tool " << tool.attribute("Filename").value(); + std::cout << ": AllowRemote " << tool.attribute("AllowRemote").as_bool(); + std::cout << ", Timeout " << tool.attribute("Timeout").as_int(); + std::cout << ", Description '" << tool.child_value("Description") << "'\n"; + } + //] + + std::cout << std::endl; + + //[code_traverse_base_contents + std::cout << "Tool for *.dae generation: " << tools.find_child_by_attribute("Tool", "OutputFileMasks", "*.dae").attribute("Filename").value() << "\n"; + + for (pugi::xml_node tool = tools.child("Tool"); tool; tool = tool.next_sibling("Tool")) + { + std::cout << "Tool " << tool.attribute("Filename").value() << "\n"; + } + //] +} + +// vim:et diff --git a/LifeGraphics/pugixml/docs/samples/traverse_iter.cpp b/LifeGraphics/pugixml/docs/samples/traverse_iter.cpp new file mode 100644 index 0000000..90e0dc6 --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/traverse_iter.cpp @@ -0,0 +1,27 @@ +#include "pugixml.hpp" + +#include + +int main() +{ + pugi::xml_document doc; + if (!doc.load_file("xgconsole.xml")) return -1; + + pugi::xml_node tools = doc.child("Profile").child("Tools"); + + //[code_traverse_iter + for (pugi::xml_node_iterator it = tools.begin(); it != tools.end(); ++it) + { + std::cout << "Tool:"; + + for (pugi::xml_attribute_iterator ait = it->attributes_begin(); ait != it->attributes_end(); ++ait) + { + std::cout << " " << ait->name() << "=" << ait->value(); + } + + std::cout << std::endl; + } + //] +} + +// vim:et diff --git a/LifeGraphics/pugixml/docs/samples/traverse_predicate.cpp b/LifeGraphics/pugixml/docs/samples/traverse_predicate.cpp new file mode 100644 index 0000000..9d8ded0 --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/traverse_predicate.cpp @@ -0,0 +1,48 @@ +#include "pugixml.hpp" + +#include +#include + +//[code_traverse_predicate_decl +bool small_timeout(pugi::xml_node node) +{ + return node.attribute("Timeout").as_int() < 20; +} + +struct allow_remote_predicate +{ + bool operator()(pugi::xml_attribute attr) const + { + return strcmp(attr.name(), "AllowRemote") == 0; + } + + bool operator()(pugi::xml_node node) const + { + return node.attribute("AllowRemote").as_bool(); + } +}; +//] + +int main() +{ + pugi::xml_document doc; + if (!doc.load_file("xgconsole.xml")) return -1; + + pugi::xml_node tools = doc.child("Profile").child("Tools"); + + //[code_traverse_predicate_find + // Find child via predicate (looks for direct children only) + std::cout << tools.find_child(allow_remote_predicate()).attribute("Filename").value() << std::endl; + + // Find node via predicate (looks for all descendants in depth-first order) + std::cout << doc.find_node(allow_remote_predicate()).attribute("Filename").value() << std::endl; + + // Find attribute via predicate + std::cout << tools.last_child().find_attribute(allow_remote_predicate()).value() << std::endl; + + // We can use simple functions instead of function objects + std::cout << tools.find_child(small_timeout).attribute("Filename").value() << std::endl; + //] +} + +// vim:et diff --git a/LifeGraphics/pugixml/docs/samples/traverse_walker.cpp b/LifeGraphics/pugixml/docs/samples/traverse_walker.cpp new file mode 100644 index 0000000..cb99902 --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/traverse_walker.cpp @@ -0,0 +1,35 @@ +#include "pugixml.hpp" + +#include + +const char* node_types[] = +{ + "null", "document", "element", "pcdata", "cdata", "comment", "pi", "declaration" +}; + +//[code_traverse_walker_impl +struct simple_walker: pugi::xml_tree_walker +{ + virtual bool for_each(pugi::xml_node& node) + { + for (int i = 0; i < depth(); ++i) std::cout << " "; // indentation + + std::cout << node_types[node.type()] << ": name='" << node.name() << "', value='" << node.value() << "'\n"; + + return true; // continue traversal + } +}; +//] + +int main() +{ + pugi::xml_document doc; + if (!doc.load_file("tree.xml")) return -1; + + //[code_traverse_walker_traverse + simple_walker walker; + doc.traverse(walker); + //] +} + +// vim:et diff --git a/LifeGraphics/pugixml/docs/samples/tree.xml b/LifeGraphics/pugixml/docs/samples/tree.xml new file mode 100644 index 0000000..b33267a --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/tree.xml @@ -0,0 +1,12 @@ + + + + some text + + some more text + + + + + + diff --git a/LifeGraphics/pugixml/docs/samples/weekly-shift_jis.xml b/LifeGraphics/pugixml/docs/samples/weekly-shift_jis.xml new file mode 100644 index 0000000..097e374 --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/weekly-shift_jis.xml @@ -0,0 +1,78 @@ + + + +<�T•ñ> + <”NŒŽ�T> + <”N“x>1997 + <ŒŽ“x>1 + <�T>1 + + + <Ž�–¼> + <Ž�>ŽR“c + <–¼>‘¾˜Y + + + <‹Æ–±•ñ��ƒŠƒXƒg> + <‹Æ–±•ñ��> + <‹Æ–±–¼>XMLƒGƒfƒBƒ^�[‚Ì�ì�¬ + <‹Æ–±ƒR�[ƒh>X3355-23 + <�H�”ŠÇ—�> + <Œ©�Ï‚à‚è�H�”>1600 + <ŽÀ�Ñ�H�”>320 + <“–ŒŽŒ©�Ï‚à‚è�H�”>160 + <“–ŒŽŽÀ�Ñ�H�”>24 + + <—\’è�€–ÚƒŠƒXƒg> + <—\’è�€–Ú> +

XMLƒGƒfƒBƒ^�[‚ÌŠî–{Žd—l‚Ì�ì�¬

+ + + <ŽÀŽ{Ž–�€ƒŠƒXƒg> + <ŽÀŽ{Ž–�€> +

XMLƒGƒfƒBƒ^�[‚ÌŠî–{Žd—l‚Ì�ì�¬

+ + <ŽÀŽ{Ž–�€> +

‹£�‡‘¼ŽÐ�»•i‚Ì‹@”\’²�¸

+ + + <�ã’·‚Ö‚Ì—v�¿Ž–�€ƒŠƒXƒg> + <�ã’·‚Ö‚Ì—v�¿Ž–�€> +

“Á‚ɂȂµ

+ + + <–â‘è“_‘Î�ô> +

XML‚Ƃ͉½‚©‚í‚©‚ç‚È‚¢�B

+ + + + <‹Æ–±•ñ��> + <‹Æ–±–¼>ŒŸ�õƒGƒ“ƒWƒ“‚ÌŠJ”­ + <‹Æ–±ƒR�[ƒh>S8821-76 + <�H�”ŠÇ—�> + <Œ©�Ï‚à‚è�H�”>120 + <ŽÀ�Ñ�H�”>6 + <“–ŒŽŒ©�Ï‚à‚è�H�”>32 + <“–ŒŽŽÀ�Ñ�H�”>2 + + <—\’è�€–ÚƒŠƒXƒg> + <—\’è�€–Ú> +

goo‚Ì‹@”\‚𒲂ׂĂ݂é

+ + + <ŽÀŽ{Ž–�€ƒŠƒXƒg> + <ŽÀŽ{Ž–�€> +

�X‚É�A‚Ç‚¤‚¢‚¤ŒŸ�õƒGƒ“ƒWƒ“‚ª‚ ‚é‚©’²�¸‚·‚é

+ + + <�ã’·‚Ö‚Ì—v�¿Ž–�€ƒŠƒXƒg> + <�ã’·‚Ö‚Ì—v�¿Ž–�€> +

ŠJ”­‚ð‚·‚é‚̂͂߂ñ‚Ç‚¤‚Ȃ̂Å�AYahoo!‚𔃎û‚µ‚ĉº‚³‚¢�B

+ + + <–â‘è“_‘Î�ô> +

ŒŸ�õƒGƒ“ƒWƒ“‚ŎԂ𑖂点‚邱‚Æ‚ª‚Å‚«‚È‚¢�B�i—v’²�¸�j

+ + + + diff --git a/LifeGraphics/pugixml/docs/samples/weekly-utf-16.xml b/LifeGraphics/pugixml/docs/samples/weekly-utf-16.xml new file mode 100644 index 0000000000000000000000000000000000000000..6c8622aa4311bb434668336a57dbdd253eb25a3d GIT binary patch literal 3186 zcmcguOHUI~6h4?>Du&<>Z~-ihg;P*`WGqpEg+MX32&4$nP)d2Uh~*t0xB}{fU`U7? zVoZ#CW0ZvCqA+LqWEVgqMBw)#S+_%ko_OEsFMRphcGLMP+3 zVk!;%kYDh9Ug2LHZ)(4=N94CHtZX()q2WiceTB%aREc#E%S#n_Gd*qnAKvFaMFuDD zC3m`@lE`LK6ODYGXN4VX#J*rG#8otEWa%AVy5cuID}hJ)Ib^Orox!L_iJi`hoGv{Iv@}WZ--R%ghiHa7=h{V-NpQouddUzr(s!X`YF4D^C%{zOuFSqbbsJ&?8DZ~ zrJag09ico+9@JrJwOEbtQsh}6wV1E9Fz1)H`q$N1Xf%0jv>LtL)rs#RPvG6Se|kOi zoa4Qn*VboOqCds#v-L7dc6eG^ZKsSt!NMqyaeQWK_D$}ds@Az`8XD(5?&X$TRcs@! z*csc!YBV=3HViI@Hzv<&5E_ns+{1~cX0GSk_{{)q4e!w61qy9!=oFrG?teqkiMd-# z>I8?z_FgUpgRb#Y{Nf9#kqLohoBSE1!&nvIIWPX1sw~gH$e$()Dh%hW(yqwCRT7Eg z+tuEM9R=&6dKZm0f+LIV>yW8_i2fg0)1c#+cn@wr5!_S6&8G)J%rK z=S3YH=LApj9a(cPzM`Cm+w6Jl0Ws|op1<)VxXLZu9oWsySvkm>sKSOWSj1!%D}5)l zyp}P!j9Eo*z2^CwgJ8`tDhzTDH=wS@G>2|_@SlPyZtl|VP2kgJh0cXB9ny?SAm6&b F{{~s7f^`4@ literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/docs/samples/weekly-utf-8.xml b/LifeGraphics/pugixml/docs/samples/weekly-utf-8.xml new file mode 100644 index 0000000..d55474e --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/weekly-utf-8.xml @@ -0,0 +1,78 @@ + + + +<週報> + <年月週> + <年度>1997 + <月度>1 + <週>1 + + + <��> + <�>山田 + <�>太郎 + + + <業務報告リスト> + <業務報告> + <業務�>XMLエディター�作� + <業務コード>X3355-23 + <工数管�> + <見�もり工数>1600 + <実績工数>320 + <当月見�もり工数>160 + <当月実績工数>24 + + <予定項目リスト> + <予定項目> +

XMLエディター�基本仕様�作�

+ + + <実施事項リスト> + <実施事項> +

XMLエディター�基本仕様�作�

+ + <実施事項> +

競�他社製��機能調査

+ + + <上長���請事項リスト> + <上長���請事項> +

特���

+ + + <�題点対策> +

XML��何���ら��。

+ + + + <業務報告> + <業務�>検索エンジン�開発 + <業務コード>S8821-76 + <工数管�> + <見�もり工数>120 + <実績工数>6 + <当月見�もり工数>32 + <当月実績工数>2 + + <予定項目リスト> + <予定項目> +

goo�機能を調���る

+ + + <実施事項リスト> + <実施事項> +

更������検索エンジン��る�調査�る

+ + + <上長���請事項リスト> + <上長���請事項> +

開発を�る���ん������Yahoo!を買���下��。

+ + + <�題点対策> +

検索エンジン�車を走ら�る�������。(�調査)

+ + + + diff --git a/LifeGraphics/pugixml/docs/samples/xgconsole.xml b/LifeGraphics/pugixml/docs/samples/xgconsole.xml new file mode 100644 index 0000000..b2bf5a2 --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/xgconsole.xml @@ -0,0 +1,12 @@ + + + + + Jamplus build system + + + + + + + diff --git a/LifeGraphics/pugixml/docs/samples/xpath_error.cpp b/LifeGraphics/pugixml/docs/samples/xpath_error.cpp new file mode 100644 index 0000000..6cb6f4f --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/xpath_error.cpp @@ -0,0 +1,43 @@ +#include "pugixml.hpp" + +#include + +int main() +{ + pugi::xml_document doc; + if (!doc.load_file("xgconsole.xml")) return -1; + +//[code_xpath_error + // Exception is thrown for incorrect query syntax + try + { + doc.select_nodes("//nodes[#true()]"); + } + catch (const pugi::xpath_exception& e) + { + std::cout << "Select failed: " << e.what() << std::endl; + } + + // Exception is thrown for incorrect query semantics + try + { + doc.select_nodes("(123)/next"); + } + catch (const pugi::xpath_exception& e) + { + std::cout << "Select failed: " << e.what() << std::endl; + } + + // Exception is thrown for query with incorrect return type + try + { + doc.select_nodes("123"); + } + catch (const pugi::xpath_exception& e) + { + std::cout << "Select failed: " << e.what() << std::endl; + } +//] +} + +// vim:et diff --git a/LifeGraphics/pugixml/docs/samples/xpath_query.cpp b/LifeGraphics/pugixml/docs/samples/xpath_query.cpp new file mode 100644 index 0000000..c622a9c --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/xpath_query.cpp @@ -0,0 +1,36 @@ +#include "pugixml.hpp" + +#include +#include + +int main() +{ + pugi::xml_document doc; + if (!doc.load_file("xgconsole.xml")) return -1; + +//[code_xpath_query + // Select nodes via compiled query + pugi::xpath_query query_remote_tools("/Profile/Tools/Tool[@AllowRemote='true']"); + + pugi::xpath_node_set tools = query_remote_tools.evaluate_node_set(doc); + std::cout << "Remote tool: "; + tools[2].node().print(std::cout); + + // Evaluate numbers via compiled query + pugi::xpath_query query_timeouts("sum(//Tool/@Timeout)"); + std::cout << query_timeouts.evaluate_number(doc) << std::endl; + + // Evaluate strings via compiled query for different context nodes + pugi::xpath_query query_name_valid("string-length(substring-before(@Filename, '_')) > 0 and @OutputFileMasks"); + pugi::xpath_query query_name("concat(substring-before(@Filename, '_'), ' produces ', @OutputFileMasks)"); + + for (pugi::xml_node tool = doc.first_element_by_path("Profile/Tools/Tool"); tool; tool = tool.next_sibling()) + { + std::string s = query_name.evaluate_string(tool); + + if (query_name_valid.evaluate_boolean(tool)) std::cout << s << std::endl; + } +//] +} + +// vim:et diff --git a/LifeGraphics/pugixml/docs/samples/xpath_select.cpp b/LifeGraphics/pugixml/docs/samples/xpath_select.cpp new file mode 100644 index 0000000..c098bd1 --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/xpath_select.cpp @@ -0,0 +1,27 @@ +#include "pugixml.hpp" + +#include + +int main() +{ + pugi::xml_document doc; + if (!doc.load_file("xgconsole.xml")) return -1; + +//[code_xpath_select + pugi::xpath_node_set tools = doc.select_nodes("/Profile/Tools/Tool[@AllowRemote='true' and @DeriveCaptionFrom='lastparam']"); + + std::cout << "Tools:"; + + for (pugi::xpath_node_set::const_iterator it = tools.begin(); it != tools.end(); ++it) + { + pugi::xpath_node node = *it; + std::cout << " " << node.node().attribute("Filename").value(); + } + + pugi::xpath_node build_tool = doc.select_single_node("//Tool[contains(Description, 'build system')]"); + + std::cout << "\nBuild tool: " << build_tool.node().attribute("Filename").value() << "\n"; +//] +} + +// vim:et diff --git a/LifeGraphics/pugixml/docs/samples/xpath_variables.cpp b/LifeGraphics/pugixml/docs/samples/xpath_variables.cpp new file mode 100644 index 0000000..c6e9c51 --- /dev/null +++ b/LifeGraphics/pugixml/docs/samples/xpath_variables.cpp @@ -0,0 +1,38 @@ +#include "pugixml.hpp" + +#include +#include + +int main() +{ + pugi::xml_document doc; + if (!doc.load_file("xgconsole.xml")) return -1; + +//[code_xpath_variables + // Select nodes via compiled query + pugi::xpath_variable_set vars; + vars.add("remote", pugi::xpath_type_boolean); + + pugi::xpath_query query_remote_tools("/Profile/Tools/Tool[@AllowRemote = string($remote)]", &vars); + + vars.set("remote", true); + pugi::xpath_node_set tools_remote = query_remote_tools.evaluate_node_set(doc); + + vars.set("remote", false); + pugi::xpath_node_set tools_local = query_remote_tools.evaluate_node_set(doc); + + std::cout << "Remote tool: "; + tools_remote[2].node().print(std::cout); + + std::cout << "Local tool: "; + tools_local[0].node().print(std::cout); + + // You can pass the context directly to select_nodes/select_single_node + pugi::xpath_node_set tools_local_imm = doc.select_nodes("/Profile/Tools/Tool[@AllowRemote = string($remote)]", &vars); + + std::cout << "Local tool imm: "; + tools_local_imm[0].node().print(std::cout); +//] +} + +// vim:et diff --git a/LifeGraphics/pugixml/lib/pugixml_s.lib b/LifeGraphics/pugixml/lib/pugixml_s.lib new file mode 100644 index 0000000000000000000000000000000000000000..5f7922defaec91d37a3ae7942b4ee7ed6fe0f139 GIT binary patch literal 1025068 zcmeFadyK8ymKU_|rD<+oy6Fox&7%P?xCKSLUC({?RziL2{iyZ2XPj^0b6Vy8q&1dB4CUD z#+Z+9KEH3Ss&&ulm|f@Wb9Q}m%rVCtbIdWv9COSuf9-FcuFhZomM?$*SM8tu!@Va@ zo*q1X`qciuCx3nK_}S4u|NRHQ{OHlQ{q09z?7si*&pRHxlYw_K@JK~pw zo?cxo&!?N^Dr;tY_~*AiVz~Te{xS-Qq5||;BFbm!DBmAtvv1r^uky_@JE`rxIXiyu z?BMC%lPvpsy+w476rF&)nyuGqP&p&5hFvT-0yLrh58vEOH?Q;cW;I{Da!^-v!mhfn zdH=p$U^5`ACeE&I;LWSpniqTpY;{W-KCcNWnn8%9cui`}9{vbJ%!-$foyQafx%B5vByUJb!Cg84#;3lGt2j}&-f&|%6LCE6M*=m!Y z-CkbKR{8Dba{o#8VtKR4FQ->)R|Fi1UL2xtdWF=*@NZVP_d(Cq;@A%l zzO~dV{JW$cOwVVFi}`esukWTeS;u4ZN0VA-QtwDTf=S0?=GNS17dy57y({0y`6 zZo0ZyXPC=k67yWYxqUUyvJeIzYELd*7qhF`W|j*qpH5fv>DkpRld3`eo}Vq3SF`ED zLGAqdaSeTp)l|cs)*f{yU(Ys93(l{>26fdfTfDtKo2{G%oL|H3iHh5`7G9_AB{tZa ztf*SMoG&i&=>|*C+3g0ywwPYe);H7hS$?~k=Qpd_#T-VJH%H%GUy0R0NDQo*69^JM z7O3`dc`ABjL2%rWcpzO-87rsk8tQ1eIDD97d40j66Obyh zZxwTWVO1KZoTAX^+nn5nm=HFbj-PXki4YX($+w6v^F{PIg45`^fCWyX0+^R@y zE=l#5P_r{86CojFO6{*Vvm2NI8O*=woB0}kzI^lcW|lq1?#!9RrEZ;CjSYBq0jkNI zucmjVPp8Dq6KY;L7vd!Df4(H{qjOTOqFg-qd`Ub=Ao1|?CGjwU#G}ub#G?cfk3U}$ zj}u6I^7)eZB!R@IpD&3|6G(ja`I7i7fyA1-@hx`&?_77I{c6oP_?(GE`_)7aKW8G* zzD4Ba{0ip`4_~W4qRoGJv39(0(pP|;4|e*rvS&wy9abEf!dVxf>T{u;i4BTU{o?b|asjnJ*F?$3nDjxio#^3G}Bp2=y1ZoO~#OqlF!Cn8MV zIqlmsIgQY*cRrs9lbsDwgvmRneS0RS5xVu>?K5Grv!#nLdFQlm&*U^hx8Hq!CR}zm zr4csop8DbGaKNh$@W>DX2< zswd;XIBKofv4_J)9p@V&I%;%$<(y6E013vqMYO9CFUzR6sA{)qcvob&MTl}A%H##2 zE7(PYz~0-LIF=D^jS#9Ru*7(~n12JIlA2e>7<%YJK=?){)8f427O^Cki1FG`wADEu z7|C#F6(hwF^{NCxKJ^bNxE^tR>fkAWLd6^faG)rFWG^}R`Rlco8yF^iQx1;&r5o{N zh=hj)WEp5ao~{-uq>Dg5qF@-{FPu-3NmK}qAO@P*83Xa_D-M+*Fkuu1E7mI#(qr|z z2tuQ#IBd?K_JQc^u0If&I#dS}ABZW)tZ1B(#V+t%1;$nK>s}ZYZE8lbP)aGt2g~#M z;&KU56d}4`g@7|f0Zb18UfwL%Sy#+B!k71jNNQN!l4f-N9I-GfiUFp`ZprnecrhlJ z^@wbw3{#X(1+f%zDPzv|f>bS2qS%tCQkb@+>J0IDJpB!#jfo#9)@%-x2`P|4_OF*$ zh<|i+H}z$LAm}HxkD#grmsu9XXz9U}f-+mJmJ~t>uf8toLQ>>+#O=OXuHI@LCse*v zHO+;HOuD9wbK~ScQsCNF!a1=nn+SrUcq3nV-cvEf*B?;Jdg%=(?XG% zWisYyZ*{xa%&&31g&0%tgnJ;Bbp%9@+nQ&`eJ2KP8}X_U!ICU~5j;waBWr-sV`FML zv?v_Ow8$R4MIgs@8E4l)2qlap5&JqiY>$qkiiOn|4~oMA$o1?R5{&TIS5#Y2!c}#| zx-hB4#NjGB0-YjjZ_nvkQh7Zgi5Av;zL~8!C|RDL@w|+om$Z^eR@$bERSLY-GN^^L z(kHg?g0zS5ggPPn?{Uavs?GU=Q9?`2F7mq-@ggvRW&E)$7mYXp#fJvxRVb}Ar>XA+ z&8kqb&!);4T$6N@H-m!jSjeFS zk%mBl--vZr3awNr-V0-eij+8clUJq77)VO*8CE^I94S{`x%pMsm-F-K&4yOu&?Hbh zWd&R)H$g>s*G*i1c|<0ekFC8 zHFoC=ZiU+CTY)CK+-_5S2gr`~$KB_bM{*|VZFMnHPF<-9bswmhLu>s3H$kn@E6DXg zLi!p5qOJ)XY%2J!git{CfbfYAY4sca4PRJ-x|*^rkiO% z`I$YB3L7Ze3wF<(!)3J}R9RW=LKdRUm_1R$Be_Z&vQTm4{;VtC3L6`bJmTdHFW^Hr zF{0S%UTq--L3+zOzrU5|j~j5RR66=}rJ!36*mmJ=a?wN$hB9}PqSSH@4V56E3ELY~ zl=w~_fhy=rN%rt0;D~m!*npN`NwU|p?Cl48qi`fs;<#g8X!cgQV?;+>CrnY_cdD3W zA)iW7?s`8&TLBM6QN`KJZ#k-w+R0pW>Z?&GwY-awjMPqV-E%M$e4+hG^`2EQm|)e8 z)74}9ROoMM?1ro@O{mD+(ge$)9&zbcHf})hGOL&6lC6O33B$YAoQp4D(3sWTRoyJB zUXl03Vppsr%6f{dJT81-E%87~P0ItMfWnp$8F<9D@ese`MlU%~aej03P(_c8CTu*I zrOMLV5k_T1yXc~`p=1g1Y2@v??;{g{UBQ`B7_m3y`{9LouGf5lN9^dzVRSc3Ta$*9 zJTUd;)$8DUL3ET;i=~%~&3%ATq7891_1!XSKIp=y(H0Hauz@@e49aCh-RWuV;ku+L z=9P=-ev=+)$9N{!it#jddC19veilX8Tnq5}$z$u-5WVd4tf|j0GRGw8i2`TkPP01d zt;yH(I`aM~ukJzCEkzgQf;20W5vJKCTcf)_eUT|L`8pOAd3vC=tm4+b*a+6Cu00pb zYE%-cVeR6j-X?Jt17JvnE@Q0b$%6@+g3i&KlFE-5O~c}VH*ISz+IOR)nhTKDq9S+F zam&N3ly#Un61-_43ABTi!~#+6aK%VFvX0qK%R&-p2kDS7wqm8N1Qz;;`^&HqIy{^a zI0IdK8#*h2UBr?0O*$E81kOO$3FstI%o=WJbtAH9F%UHsDIcXEVW1Ku;}j$eRDxuZ zf`oxeki4(9Fn)3x$MlxVm^bN#h5UXn!N7JX5|8Q?A=X3}3W$gKjgZk_`6V!L#l~9y~90{s4;4n}{{+YZgArbWVxn`-g z%k>M)J#L`X+TrSQHpkrksw{0}`QBb*G#S-v-39_MYBlCAectcXJGIeZg3P|w_k((? z*J@9CNS64x(SBu@HHO1luia{+Y%<25iCx@m)%&9{a{h`U0x?=VXiiT1wISDTv@h(k zR;SVL_Qv&W?i94g&EcTlYh}xoU86JX)*6#G(b8z&x@CiQy*|qB&g}2~=A@IY?2m`_ z@uWZL)rQ%0VYhwQXt#TyxR$MNuPsD}?N)!-86$Dz#r#vJq&*%rM!jZsy>zhk8vV&| z*sW)y>Bz1#>Vq-OA!XII3nu+;chqY2GKiK_ayqGx8nr=ovOIY_7~qdR7Dx5=Y4dc@ zIt6=bu#M?YV91Udy(9(A_7}xss=4msdgr7F@VQX0Hcbff-RG?in={HB6!D&BR&#o?= zq5%ZD2id#Ap{!MO+?jMnO=89Q>-m*ED961qn(Mb3+4&NIPIkRPZ89DWIvHndwTp(m zVePar%2uf5V4T$Z5Sz{*yS!RDGJ8CkwA$m&DUau3ZJ~V97!G=^Zks4sAU~vC*s6C% z^>(k8i9AhqVXx8Y58AyUY&qi5Kgh{Li9-)q%I?OwBA&(3G_E4S)< zoqlK3>bIMq=$f*9*>yYP`nWsn*D{%h)GB@2>Nfk(7DJZYLr+YewkG{Sx7Y7vQ0OPe z=kAz2?bciE<``s87w0%YcNqD!H)>5fr`>KgTAmzpXqw%8|8zJWbQ(nZVlzdaLc4fe z>kONxwRVPBPfj~!6+eRtoYaTSQFggRRKT6fm}m7tv(XxkyV=RJ-U8`9=bN`Sf^MhR zs@M9Nw!Q7*)5)+?Yma&?;hY-OF2|G{k06uC6?%^Je9n+Q1JeifQ41^P?O9;RpN%m| z#;x8Mb8EU-Um<4ELSL_Cbuhm@#9w+yRJcMbo0DVSYq)@|!D(+iYMzQ=553?v97`aa%{Go*bSW z&X$*FbD)R!*dB>?HtBXxheId=70F8Qn7Cp1PSzN-o83_jOF(moKV){F98^!;3YCpD+B@yGd#97EMS-d($F#ksnR6Uc zUiEHv+J_jNVx4}mfc!6CEg-QF8(!;*RPd zeFb5>p09bf*<_t&oz?(rS$~{0mbX}Lh0w|a81PC&Y}u6tkl$gmHSW}%&R^ax&M6p& zmWV6c1gZ}+vS2&4Zr>a8Mzi)ojYL^gHmy)th5cc-*Xp!J7J(F~wYr`!Kq0z^|9A`^ zWrI_!`Q2gNYj0B4;5cj5dxJ(3)8Ca%=BCW0DcZG2g)n!U-F^+WsjP`3E?y6gFIUTJ zr~*iNtw?vnOF1c>FO;S zegnmcskGFVB-Z%xpxbFrpl_a2SB_tkrre;@XQ(;7LHc2u*z`e6?sZYOl{K5Ejb^Vl zWJ?Hfn-xzxJu<6EVC^*r&CI4yKvI*C7#(kln&9c=bX;q| z2yAQNE+e_9+JiVuYNO_8l1)?=@zU;^Rl(MNuQl-Xqw;xxN~ZN}Q>R4{vQi z%d`~l!I_IC)>bjIXLK2lYz$^`Q4?Z6nA8XTQ3EXEDZIQ95#V8(&6Sw zuBlRsiW6vreye{P*CMns+^xujIKDCKbtf%6Niu1n!A};;JM>VUzGA5>{?3cz@m$x0 zVT4Usqc*OMys1*~l{h*@?7LA9l(wO}VFV|&c%fT_)d$PCTZ2x{#)~UVi7WQvUST+R zDI!ICM*>f?0Aj0a4zl*R-|t{i%}x%di{;|&_43v>kOoRR}NW|tl4eVvG1683#5r` zl6AV!H)HIZc=saLHfKrSKkmb=FdSj+B!bzzWzU02{{Hc>-)zIy88OfumDxaW0FD#h^16|yB(;Yqb&rZR7Xk3pfL`@hpg)e6V4CTENXPKk2@5N(d$m0(15CIZTb6q zNFnbIYxb1)&)LejiQhQ>*4@&b(zmzT@+@pRcx_1P+~GURBW-i00^oLF3ua+ z8?NWC@XXf(!|Oll!P$lQ%_zobEKW(7B#>gD8SCI#2fjNic*Ybr#c4vD}%X0bd!y+AH31QW1Q4 z96@v#gh33E5kY`JF<+c-O#yAzniRkktzSg|VaQ)f6!4opl!_>dZ3q#w>tSjlAN53d zbKVJ{i&9gf#|n&HyqHHTH^wXPE!Sr%70kiQBl>+qFZV&Z>KGRso<+3^$LIlK?oBq_ zNBanISb9Fl8D@d1`4xp-Lj#!-g_Dk$KwG`GvKZ;`<-B)u`9`Bl3q`FV@=*>#uy%A5 z#Oy$iP;4$pjXp;fz)b-CQPv!?6a4=H(V zgZPHiPjD!N3@F}>89WvsMtk`71^^G$`)bzj0vl;Wp+kb$qGM#q41^IfDpahYQg(!q zNR&!yw8VU(`4yOYnYx? zMsM0=c#v>_vBGGOE3c?;l$+U`9VC1V#Xp~|R62K@DCjo4f1~yIu zL!PpvR`bM8K)IXvX0Na=Aa_B)?oxB*`Q`!#bAid*zTRA3;mlYqoFB|DXs?i)_lUI? zm4y$p5I(|!&dWduTXg!Cs67{su&fq5C+nR2;*yd@k6)k4GzYy zE)v8S$2|l!Se|2?XWBj_CCMj2;%bSYnlE_-@=3sLPXZ(0VOEg+R#Ag;!O46Q)~z(z z1(CjnRvUR7g=}q7fAhT#aZO5=*LlXPctzj{1r(O0IirISN`T8PnYpcOKB+Wl%jMawFp}x$oZILwvssC8rsQeS z-nyBlM5Ykke*}NMrtQx5Bf`1fD)|=R80%0?fRfXy0VqW6E*DQk=Nx(KF()6nT^76% zlYH?uyWLzq4W~bO7NOBJjc$+*(ci8)HY1=AZv()GsOMS>SC_E=!wya++WZ!v z6<)BNA{M^CFv{(X_*izP@yX15gVNgWl5#01wXVBHC3vK47g;j@jk;!1c4w97hFJ+vBPW=FVY z@~R-OOHqZ#kX^x;+H#qNvj<7# zbVtla)(>bUj2|gtaj#dbMOq4_e9|Q^HW$`NvT=)uxEPp%!qo$~Q|w{UxP}td9Pf8W zK=@Mebe-Rp!0(~b_n zjR9pkNjP3?94N$XAkNH%oL}GgO68%fG+Utqi;^+ghR3`o^oJ>uug2O@vGY-n(XMGy zT5+8d=1^|5ka%w0bu=Svj@_Kf32TDW$t!zemxv7|zI(LNOxY!3J%~Fw@)XQ1AF-_k z0MVEJa5i(9Ta?&Tr;-!rxJxux(b1ZPdLO=6&&UknON zFf0yo&8dVV2{^yP-3|zl?JR*jVoQ&9Qbj2yDUo*=0NL{pMKO>=01valZe(zSg0lLg6@UWIWd$)z>L1A{0fk3O7351^S@uZEN1TAouW*g~ zl=Jm^n&SczZcSn5Wb+RB@kX>*B8-MsO-@qdV+lGi*ebPV$$kc0a*J9xL_`AM7}A{Y zc1`_l1UPK<$(>|gM^sKT)ly{zo{mTnESuplFmpY33zG6MI9U|2-fd>nK`rvP(se~$$?1`S3a&ohLpFx)bX83+D3DuktuRgdwE1fctm9* z*E?A+r>BNW>w;NCDd%stWW*Lf_S_c_5rHvjZoO@mnqmR@n=7T5~!HFV*{t<;@1q#9iUo2qR-Nh+^rG!Gh<;!yBQr z8aru2M!edzmiL1^^XLiwC&nX*VexiiU5Pbwc(D6%>AB-_-$r7c!PsxtMx0+!`C^9k z{BbGmOT3*}S7OaPjSdTD(mm%(%N>`}M&f-b;aO)w=|E~7TbASPfSdPf2NFQ-$oYRc zy}jBf{TDh`6juN*W&*HKo{m-#iF*a@SGeMyi&aD)jN9`X(6FU=G4r0-)Fz3pD_nUO zcrN2LtA2m)h8S3OB~wz0)m%KQLE4y)#dF-`Mkmcpn^^2jV5X%Ce6w1LbBu^-faV2A zm2E&Pl;9OtC{BGsTrE5E2^?OSczGY|v0g4|yg^3$rDw&AZT@P$S?}**)q-g|TOq#d zYJQDRnHk@85rvZLKgUMJq0H#6IFuRO6^AloyW&u0XjdG{jO>a-nL$x;C^IA~4rPXQ z#i7iwsW_Aw)D?%aV!BGe(&)D$x=OIUGy=agSU$SFHnyLJ(4>f;5914;595oT5914- z595oS5914+595oR5914*591G)5914)595oP5914(4-*ix08k3m0TBye@&zmliq|rq z(>)sdn$1vnpJ-=@O=p`GVGJ-IwXt=feGPm{_RStCf56s6(zpr`Y{T&H9{Iz=plX9e z=;S3Jzexj#sOKISDA(uHMM;O-3@@8!SOf&cxMKltmXs2LwzF2$B6nTli-SkxVgOQb z3c0ZXE!tSbXm=gNX@F`0JIAWO*yJ*?4%=Xk1yr0~NY^=g6PB{#a^=65i;%TC1=I*D zkZXwp6sGDH*>rGIgY#n-F^h7aZ5uQVc7?&&qYcMmH!s~1_rMlKHoLyr2qRD*suY%` zI1wp8jBe@k5?RV7qA-4W0$^XYIYDv_fFKc<3X zGW5B$w>#M0TbAJkCqDIc`-*bq6H;mZXbX@R(4V*z!0ujKaejqr_g$ zfIDvz&?p<@L6SrBn%oCeS8Gzi6sNK#PJ?k|Z85waI&;_@WaWfdW8>5W8R|$+f^9eT zN;y#0FxEySpDW{^9PC(xnBbIb!gz6#J1EXS!~>vu!st^IGssaq5GhWW`0}fB)~MD$ zDoY9~KvLY2D;hPJ@M;okZ<3B{rMie}rE^v%dQMnVY=TQ5YlPQPYr@E(q$P|oRI!22 zK~&HRW>!(L{2;x6la?m2och2PXN22wl-|rzuGEOzVV=o6aIxV0h)(Gf>5}j}jY5K_-ny1`z+teXKUa?DE^CIn5 zHzqniTokEX#Ry$}w!nVd50d5hZv)DdK6|)KS#T$t-FKT>o0GZhiTV_1CXh;Py*t~Q%Jw%dxkW+_PxO$aN zs7V+#fY|Z9JVF>gg;7Y>b8?$}jYP?w`coK$r=GGT+^!`h@M`DtC1u<|NWVrU!%*aw z5ZC;3m26d3!#?y0c97J#>aeV%&pKkYgco1ey4_F`*;o_`>=zkDQUr_;&$LAnW3RPr zvji71ARJ4InTp z;`!vQkmJPKEa;j5+?I#kigc}?&bG!R=|Di7AC`%=TL#KWLAhFa7-j zXf%;Ii=r|_?%k!o8i}ooZ7>$~=p6&kE&cL{%@LrbV$9#SxQxTu8bo77B>_jlwemD2 zrD}e)T(7-G^AWBV7x)bzuMrJ8m+w-qKe$6%|tmz_rkdZiv3~c2{-2qLRD$6yxV%sGvU5Zdt9;hLO<(3MnM5&a{T(mqGL8 z*$*Kv6$_UZ-NLeyAso7dloQ2qh!sDC#8xzc5D;0lI0%);PkeNMH(5yjibCaF7Nz)& zi?LV0J5|_}qQ9|rjM8W;227-l`Rx4o_*t|O zvpCucjm0aRImRfp%G0n!s6~SQ4fY@6Aj4ax#6c05i|kgnSCl@-iRIBp>Wae>Fh;mp z9HrX?ijW=JzsvY|P)je&EIzIDKI`7mlFqImr*|q+Np&s;r@}?5;8e&+6`Tqise)6X zBUNxJe549ag^*OisW6f%I2Dvp1*bziDgl!*s_2tSj4Is20gMT-6ag-d3b4HHK#w*> zr?3Z;jx97N=wU%)r%vmNt-Q%Oi;r6QORdlKsy(b%v&-OZJ!c_O8}smiC+*KkZHcJ5NQzu$oVudG^AnV5zINB z$6@m)nIl2cHFkQ#YCC*zK{@`)LZ(Cl?yWkHXr&5#p^HSErj**TribuG$=j-$LGW-D zPEfZk5k<|OA$Xne$do-#DIN!xtIZ=;(THH+UD^f`0bmladjBY4leUi%wsQBV;iApM z^&^rwT_e$I&M)|2ONz7=9EZS{|sm5uB%9i8Q)>Ji8E8S)V)^t+UHjl%wEv&9pr6^PvS3}nYTxOQRx-^!- zx(qd7tH|{CK`|nI$QX$}WQ;)1q*PoKhhKp^l+v1_q3~eTU@4Q!021fa(m>lT=%fl; z)H*6TZe)w_8m{;0s~Pzef=Mp%NT$99T`5f!u+~sjz*>W4!v>|v0P#oOlBQC~7>Pd4 z7=dAF!ZmU{rpehPLbvv>mz+8}7}h8P36!N8fK(Dku3nLFg+ia%;mall*)7F3MXZ64 zCt#$*7Y$->Z>&-dVbKSGAq@QZbB^cwVg^ls2C{STncEe1k$(BF0+T^18AeIfiZGnQ zqFT)Es;Md_KH@7~4oCgrn=7!*ifH@_Gf_9o~ zF2Z7~-{&6%0YBapy<~1~{eG@S80pUV*rCHz=NL z(4$>DUPA2Vj4SN+;86+`r%$9tRp{3JaMHd1_Bzkn2JmzynCL>3-^SKB{(GM_7^?a{xLHAye z{U+YghAXFNJurd=%_`2{;80F)_6ny(ITniAVOZ-50I!7V_?{e#B2$v+HJm53gyyeS zxE#hRA>Lb>Kh02O96I2gkc8T#gY9x!;NZr>PY9i}bMBbs@8fkw#jNaG~~x*wnnu93`PF75|%=-h5p z46hp-BybQGbvX}6&rk&cj~>UkUCj68&V+(lw<7@~?SswXuH+ThLDq?rQoo&0x?p3S zLdrRa4FVW<)MbGYF_#aONOu~o>9B>YYh?!RPAw;)zL#dkTRT|www{e{-)|{l<*r8P zc0qb#N5g$bfzRKPK}o@UuQ9pf=+1A29Hn61n&jC1oCn=~5H<t1Ao)m_P zgo;J&vEG%n!$;y=v+!ELOy#D^f&HL1tD&@K4s=sdaN37x<-x0qUa(iQPv^^9I5zbC zDjQfBHe#15oi}Jt)TwgHU|S)(lLk*t94sKgoisTnlmH}Mk0Q^9ciPIXkZqcXI-|w$ zo_4R4;UtF2<3qj>6eH{QY>j-xaAZKcX#aXj{JOX;d-C{_(lT~2zIngPnLKaGP*!xU z3MsYqP&!EpO+v#swK1exEt2+E(3M0=XgWf2mM((~ z;MG@UiuFxQjnyonu3W$*Y-}m#HpuzP9bmL^P#fii$|R%G=~m2s!6P`j*>KTuI&zT~ z>OoUd0w(eBjmToHp^J7~p5>xR)?#qV?FJcJ3}I|hv5y`Mms58?d?GYmrhpW33hxne zNs{kKTT6QXE<7RoE*xpY0pQt)B-1Zf-*7go7>gG^h!81 z@Kr_L!BH{{c_OGtr^M!Rr7F=O@?|rg5#o@|zgFWM;H;L&3Pz@qIf2ztAk$5u{`mrV z?BNsew2(-E4xeiQlP|tGTOlyr(N`vO6ru_R-~)NN*PJ4@eQ^K0Vu1*4=1pdAyVXoxxPc%v&TpvBLDrHZu$A@Yc>G0Ne3vxI(?|$( zVX11fPCp!jo)>`TLP>*I@EWV64%^Yky!JW>hN69S5tQ(*c%d#gF1$*HL83$_vT=QR zB#(2P`6)W7xWE<%&+8x?q*kI74zNt(aBP!Orz+soI9I_LtgXOU$U^}bA1JYX)cU@1 zI}HBIXL-;$zjC!M)zy&)*XDY z-~guHa($+sM1OgdJJuNd-{`13q=IEgkgbHw1Q_~R8+gU$R$6y5<547Y5r^3cQ%3LSuL@wKmAdtLH;arb_!O(0_*GZktz3wjDV#yf=Kb_d-Quwje)8}$Ay=fMUvodfYk z!3LIB3!m4z@(tcNXo0t*p4F8Z`tvCqEdEsup0k2+4iHfjDsV;4tYB93a)62Z=;c z*HPm|v?QxKF+<`upP1Jo-L)+v#w)nyHi8ep_p}al6q$*1O}kDXXRNB=gtm;i&7%x0 z9ULCP3Lt%NDj}g<0{C6~TMSvAH$FHy$=~0DL5X+RMUGFQjk2dFCwtB}?3@v){NWy% zcKZj9+}co|8>e#dsibb9MtBBu%g(ow+LY`47ABD$l)*^CMH&|^&Ifx|H?VYW=6C>? zQ#crX34GURd}gfQVzYYfA@M((I2=mEs1|IHep4Eia8UqP?Z6*b?yfE<#;5om=xb3J zZd;Tep|vo}QT{uX+EzUEtuXCeE&I;Kla8oUM7m zSHM=cq~Y_Lx;~)A3tnS&Y=|=oU0z5P#aRFXA6Vq_Ati>Qmvp3jK7Rc5T2H>L0}QhG zgS`F*@1f$ubA5oAH61w?ZUN)JAJ+)m7IpVP$p`xfdmOZnV~OQ8j-jy4|3>yA!b0iW zjf7J*=nTC^o{&8q!VSMJKORYko93f+eA-zwBhJNa8cK#{*K}A-xwPOuM)o2w0e4LV zHxX?-IIq7IB*=yeLKYP6t}@i}ix*im|Bj=hSgq16FNiofEsc7G)Wz^u-V_~#U2H&!*BVbAUm(C3XKunPr4Om39#FsQx6o|vhgyo& zRKtjOJS$1s9NJ!Gm9g!7gk=r95sKJ2j%Z{*RIx)zl4+x9rNA%kSP zp(h`$&;^dzwm(r=xr(PU6%nkCI96@nClRNvDejD}5JF87r?402c_IN-(_z9kIm~yJ z`2XawM;0cCI}-7vZN!35oQXvH)kF?IXCl$Q zMI@3Ogp76SuHw}*ptF^fM2WMS?s?UXX!?U9e#$6D)wYBk? zFxfFeA_B5=+P7zN8lhX;OP>jo9WyGz&OqlE#f)OU~oc8URoJQ!@ zHtT1?WXF_^FnQ;+Z_ng3LbtZMKNBW9#(0FuJEwhnCZ`d)^}gXVVY0KGh%kBQv~SPk zG(xxD`Fti!b~Z#2Chwf~?U|fL=+=9;&xFa&mM+5NozuQOlhX*@e)svAaM|6IM%cW2 z>bK`}BB|T&pg$8vyBq5WqjyjJ_KZ#>b$c(sXTs=2=fLi%-=5JXsg!U=$F_n|JsHBh zrh;O}9?sh9IJP*cwqHjFNHESVqFt@n7SOguRl7~&C`2Wguw8O89k}+vNGOvR(d5J- zCPaZHMkI7V_TqxS#u$3&G(!y_e4~?T;i*R+hx`&RRcuor~V;5 z`h%30>fkAW!i}>saEJ&i7s+07@H1WUwL+Pi%CF|&$X{AuHc1{96iXKuq4{{aT4XZp zn?NZg#0d%t=aW&0&hc&V5)i+>BFDV3>>@B>6b36iYwN`28HJ3S)IJcM-Sr0|Q-|u{ z^8+yjnH7yQa>Yai#?jD}^6Oq06>Vxpv7VArKu>tj7ne(jA}59*krQyHD1hl9K$!?x z;}a?HPWCjcF42t6pCelv-kbuvi$o=y(-yc=)2DC@sK_`J zp-N%elB$w45HxQC#hT55G9d*O4@5x4Q}W!8psHC}@3Ab1(b9t{1%;C}AXE|0I_VLlEp8AM~U%P)6}6w;Yg-Mh+%mS z51I{O2qlap5&OkRajdw0#mBa-cu2*w!AKDS3C7cx$ib+@*bXxn2V6!KefC8U*WThy z?W%fWT3T3hBykc!$@2V+giKHrKrc(k+A3|+#VSS9Y8ljG7SSg*-h#A;@Ps-c`|okc zWU9@1G1Sz0Wx^=Zxdd39QwVum%y{5}oPuT@XDdmzsWJxFB;DkVVd)ysu@P~g3p%($ zeC%y}!kFgHh3Xlj%k8=3mXD&@RKYPRX(ktJR=FAM%tkr?B(lD&@eHiot`Lny?-{_2 z!x!D)H@pi&*hFh){VOOfuv!9QHz zq{PX4z=AjSfQSysNLx8>XIS+o&3=kT{Cwd^FUS(#e-C%WY zvK>itq%?kVE5b#(SH%hyR_PBnEd;KEL0;o4O@$?;&6@AFoVeMwhAcax&jat;mE$f60`8&s6|P9A}()Gt$NCF$YZFU1kT zNT$Sb#~^R2m^k}Y?ikS#*9lY9_nj(cStvtiQSN#_L|Xw5MN!4s%qP1i1nNlbWG*`O z)hHy&RHSx#>t1%zf-h-%R>6g`?dU!g`db>iA!|z$Dl)e;!E$RqV?eM0qt~sHt$^$a z!@Jg;i%%~Gjv28p)+u*YH&Iuw$P$TK$JlKVNW4(YXVIvJ53D6F%JPNuAp00M;66eI z9)SC>!1=@%9LAnKd7D;nQe~hHThC zo`;8!xzH|^q4H`(+^MpS7&WcN;%UKVlRs9=j4(X$oL)RjVT=t_)gOi~VT>-XMid-v zqo>aVFOqs%d$=wUx!+G*O!u4gNIS+lu2zhvxw);k(9fbsJDlqJEgo@bgz@aN&acle zGRGw0N6=2{&dQx;b>K~eujlpU@=7_y4>EpeHi|CF1!-0$BTTbPwnle<`XW#qc2BM}S z<)aiN3{-+-oPvabN{~!akT6gQlK1tYSw$u#nHbbu3WP!s7V`VS1OtxWNUr(DYGR>C zJgQe%tcflV@i2e<@CDW})^~)FABH)}-&4LA83^{Io?(kod{zNF>bv7~P@i3Ag8J^e z5{iyEo$m8Fd=-`i`9CZl?- z+du$Dt;XD?&-NNV@-ngF4or3nbIULk`t!%lnYjlR)T4T~C zS{m(Jw`|a^*GJjinf<-roOH64{qe9qp7bZZ+Ay0g?6waZ?RF0o*Ru8PwT0-g-RciJ zV=d<~hTlKw8zcXs}+f7i!@6plWTL;h@xNiuHr#+Y034ND7sy_VI%{Pqxk=`GHe zE3~pXIp)2F3)mW*_Qs>;sTd})lRY{1CMGa-hK&(~sS9nfx}IL)b@G#Ee%AolZ#4&v z26$gk|7?1R;4ruCJ~%WU)J~OHA8(cz?5m~2j(R;iZ8qAi)6N*_QLm?)^Vc(o)Y%Gb zwTGP`Py6lq5ZcLT{EVMLwOg-e<3W8g>Nnbj06v2dKJye$y^#%u?bFt26N*BM%d_?T z%$raE9S%Cp)(DBri=hpJHnZkrTyKvd;_dm>?Bw`vx(4I1KpbCsItSAP^Qk)?_XgPo zp4*)*F7PJgNrd2bHXe7!!}_?b-#I%uJUN^#FVE&c5AU(fi*`2Yc29>xC<65^8o^`Y zhT%I|W6*APM>Q+~%_07f*?n?M3CnnN>}HAqA~1)5muZq zMR+mQZlISn+LJ-O1~oKa;GInvPRCLk90%1}y_XFJqw(ovO!FQSZZXBM2<3UaJU_pM zBCyvo)R>HWr!b|)*>idW{^S`%>*RPdeFb5>p09bf*<_t&oz?(rS$~{0mbX}Lh0w|a z81PC&Y}u6tkl$gmHSW}%&R^pBS=P^3BCc!`s6Ncdg6-70eQ(Sg&DsYw5@k`@v_fGO z_J`eGtJ4};1X7^Z>UzEah3Fps<1u`c4NkG=~^<=`~6{O%eN;29V)Q6|d=ExruT`9aO zHX2#)6dRRZlct!OlhZR&emAT6^>p8(>^KZFmp|nT2!1sBlKJS)3_F)mEmqh zCdBcLS+6^3;YpH73k`mmId6NV8sVU60jHu9!Q!B^raCFC~h zfzmc~H;mw<7B6&*u=-#bcWcne*?4hxu%|X^4_xl|z zs@cimbg^8#y;gVQVyM^r3oK zTdYr>-OV?zEfoXR+i&8q0CQF|oK=*GXq*jt*!Z79^t3bVezQoyam!kHpx0&=$kS2O}u-NYn!vA?;rQ! zRv3=3b`rsC-m>SxB!B;S*l)IB>x>xaj>>ExxN+SmCOVpD=U%Q0m75HaOh&2WEJF~3 zQRuqN>{8!x=9Y)tXEsd0iDm=%t~9$m>`?PzEVo*$*mSN%VZ>4@z1HY$&)e zFapMRpYdj4fZqo~Wfr{_=Usx$he0;$uQr>>iD5oeFHwZqd{{KddQAey*nC*V=EEqP z4`XaTEMqel2H0$T_t|U~2H1R9#^xi3&GNwMef?cAS{FF1*56%b>jIy#Mo~zDb#Z{X zy3A*I##@C#kuV$Ax$e{!+PCOPuUiJ?TIbAp*SjB5^4bRR4X2;rP-vC;_5&Vz5u-hPdjo)n z>U}lqcYzHqwz|+EL2S`6vSbFrXb6o~DLcYQBuXVUT4FxY{0dCH@y%?3h#EcjM$6Ug z<_hb13h;Psh<92fvl`Q2)hOSzYzu*ytfzRl8og(_|v*rg^fJ4 z31CVsSI{QYDpoa0zUqWVDHb6Cj3RLoz(#3c<1{ejDNAZKPwWJgvx#r^3hTmb6}F*; zxIf=q;9xE=dE3{U%PX82tA+D}`33D2tn`3di^{@>SqL9tLFZ*4ge^LKOVpl=(3-`x zOtYEw5e^m5v4_xJFG00E;Tjtvfp$e$0M-2olbhQj3fj1CLm~mqSd?KM;sERE#f5yr zy>OfBR1ESK18=ESF)Csq4CFl##@F=Xhi=y!9E@LGB#1ALdkAW5 zrWO;1lQ*C=prES2=|r&?M@p)Ok$c33r8}h}>CpxqKD)&W=jb`56BGWsnk-dWdtm9& z6xMh};0V8yT|rJwwPa>>viX$DqUG{zR~X53bk1#bm)WeW>aCk;N@NPr{YUWEYufH? zKO&s#jl5f>eB-gPImS9v6QJa@3Q-|yce!{XI_Jn+k2(3s?XqzNV!C*n-EJFVR%s*(#a`S-4-t z^FwJSv0xO#TU5?T`&((m^dv7fq}EKATF+Dghe-7o%>O1hDW^-2Oh}!)besh1PB4?L zNOKCGZLAen!b_lC)}iX5)eti~!ZnjGVm!bwIAjAHayugxBtpE9&r(H1Dg7AE+eS78 zT|Q*2!@JRc{dy z7X#zFsDQ%N1GrP{VbQpTYSd%N3klH?W}}pkE<5PW^3^ zlaz-Y(?tFW)&v4?euk|OjA=&);KqP5og^Hu3Jw(FHV|j#Le8&me5LYGR+_EQfknv} zZNp>U6Z*px@giG0Dt12VG1@gvN-M5&!W_zt781{`yN+gr&9R$PIblt3dURz^>=Ln| z#CMNYnkl|Nk)7AlKv>eQOS;pwtxoS{AEq4-z3Mdw!u8RhE5!#MxI`A3&TL_ zOI-C1)Z-;oRl?HEOM+`j`8A7^g#$kE)YPtXS}nFmfS?L}K}NXi_-Z2*Fud z74^xDDt>@b1(ARbqz(j;ZLdp;GaG@>il}fJE8Dsips#1hQY!X$`DU4mTdw&fT_uDF zJ4TdFyCl2{!C6ymlNczcOA5klW*O4PFc>4(oJu%mAa1%AM|^XHc|;5!IT^9r=!#R$ zbFXHU?~%G_&3>~&qsFyEd2AMPO5zHv+>a8cOb3t6Sklffh;Jh68z)g-;IK%HU@&tr zv zxnoe!kc-jvAd8h>*$H|RBTt4&g4M+uDJkmUNI@_s`7(2851@pc<&y)GBCdQ~hYTri zd#U3)leCTIMk71|A1v{-d zodk|cZMt79Z#H-)?h407=(Jr%v2@5_AzAy-MkuYuPTG(WuQsja{UFagdV>Fn@kr`R zyq#EAV$B>L?0#H&?zr5ykyvLi_S>})=T}s|m|;DCTuS>AZztB3STj$f!-AP~&-v1F z$ECE9cwb6*)|pT`kXpx<<#;>b=Dpg11W-G2{$EbR#kp<_jH1@K}f08PE^Sj(h! z1?^Y3;+~6DL?4XX^BQIhY$;yM*kI0ig;ZX7X_Dx=!j*S1yV<;E)$i}!5ChAuWJ*f0 zn%_`3A4Br7xbtp~-V9j4OiLB`X0;UO7!lI|7zId`ZBX!vD-@?bj;;ouVSv^F!=(O1;uNb&*>hGea&Vlyic?<#HO>&OIr*uAGNV{ zp?wW}O7_hjDSyD$MAEnl5NyNn?;iQX!=P$|MCjxtAiqfih^Xft7%120(?v;#+YB$8 zXIKOT#kgYuZmbZw_ zsUR>0;uB~?8cj0ST>_)f27&g4FH>}64`KjPa0#gsm zh~RTJE*;)-l|hKj9}?G*Y6~4<^5(-YS99rkt;0lc<~!i<*l(Yd&Ave~Gh$B^X^rOh1z*8Gz2nt zhPE@HhI%It2jEMl{IWo+KbgT*ltPM;aPW5(UhOVdvy?^7K%LZ@c%7}Pt?jxX$E^yf z?tp~fl5|lE9urCvTONmoQFvHml-SD|aOX_|8f9~_5UO1z1#?H$sjP`pL&*|ShRz%| z2U$5G*4Q{TL534`w%yb#}(SR0Xiu8eP8?$-#f zqjqh9RAZ=O1D}JapcS*}NW3w}uYh+&1JcqYmXkoS#TntY9Hlq2lq)sjc9?tC$uTt~B34dqqI zF;G@jxzlkoN=b;+oZG2;p;YXXR543HU%Zhh^PZN z8wlywsO0&tJmAH-K(3Ol%4)b(@dP_aYFu?#*3oAjv0B25uWQ|ID2Z$=3I+Cyj3Oxl zMyRh^bH~_gE!!-?g$#&D6GzI6kx|ot&I~Bb!cg4q&@dB7Z9t!D=Ba8o=YfZvCF6vu zF{>2&bv5q_ilAf^zhH~cFLIF`(;H_TQVDHDEYfH^qP@jEa)iWeBN=P%D;c}vjhvQ5 z7qt>J4N$CsL=vC86>^+dn+07HfZOsjXs^xu)7jRTBoh%3=Z9rt?UsRZQc$jzXV~`? zp&6F7bEw6?hRG*DwdA`Kkbs5M6Yz2w4GN{QBm?f1lgc77X$}OHSrD!r)qr9h#c3hA zzZk(SvOM2cZ?*74qlwH}6qO-z@8aaql?JhOu?@ze9=&7Wxustou{i>?RE+ui7MF22 zTZ3qb4)C}SrbR) zfW&c8R6o?q;#E1m09X{~YZ*4`jV`GwIhwDH@XvsEkZ&@9zb2S%)e4B&n~;yli4S@` zd&9J*fX-jNrYo+|@CSqh31`mh0x}p|Kv8WS{LAWI0mlLn2$%v5Ea0b+2n>&el>4}J zJ<4~cIWf}mX0RTQoF4)4wiL0unOJ`19YYV1c+lhim_#IS2{~dv2@3ER5;60Vb6Gb4 zE(|R;E;zQryh&`KDeFXO`xO;a2*9<_if)L$^LAHJT~W#1e2Ve&FjP<_fuMKT}SxEkh zLgic*rTC4Du~)!5RoIHV$E>_w%<1l_Q6AQ{E)==Kjpa>Yn!TBVJ+XF-(r7CNOr(wZ z?ELunS+o(eINA!0#VedS#wfMQ)38LSMS}hf_8;OP!&|1rK@pgX>{ho|ls?Ca<)z3l&aNP*cPbN=>Rb#?g^N_dsgRK> zI2ATh1*bwss^C=kNEMt4A*q5>VI);>Dk!50PKS6@0w!Zr(I=G{Rk()(7!zPA0$dyw zV0qnv9&L(FVGkx9TWC(u!-B?6oz@jwd6RP%AB$x{sBW&uHic)Bm>$-v*=6vyp0g0C zjd}RMQyIMy$~qpfw?9}Q*s4XQ0CDlqjT|7ui$mahZq~=QkJI@Zqxh2=L2E%1!Xl4ms z3URWW*N5Vz`RnPMT*ttAwwbbCDEC`Aq_SVCA(aJF4XJFHYDi_pR6{B|rW#UNGS!gE zmZ^qRwnsIj86I2Cd1aq3Z%J zGs|FI8p~i^h8nO{WcvG{7?D0?j6@$YMxbX>DlUq{ufQEjX-&~kcra?Pl*wfPJ?vIL z8*njfQR}GWxKSX4>wWraMm~jLl1n_2sjop-N>c@_HB=R_)?nGNL1{8T{E@e$sT49s zqK`91U|5=PjU10@ayE(3t^MmIr;ZMWHHtt2WvK=rmBf*&S0r4a&}Vk|vWY=>PaNc7%=CstFM#%14_5EutUAZY+399C;#`^9YhO1|~90*k{56tl`(< zsp;tE662;wfNtVvsiUxrl&mg6tHD)>huq)tN7+7Ig?>H5>4cvUp;rv0;13f)72rNn zuey7(k&oPJ>>{Q%sh)K7x|W?^tG6elo$4)bH#fIFft&{r(&mQXh=4gIhJtpQYc9fK zpr9(Pvs+vblv{ul+=4F)82({>OJ|DwpgM9qaKd}HEg4iKJ!>&EOSokNr!tOvVR?Cp z2f%T@d3KA8#5NNk0vwUGL>^TU3qrRUyuKTI9erg5LlA1*ITC^aPN_BgW($c|Anx1^ ziYFWNXxEOH5W6|!3cEdclmf-+6RA-Zx^+LCbnicYUpg5#qu4FEA(W+A3S3suXfadV zculw)5R-tuvCDyHFb1Ny)haTJ34TVAJV#1N-(?{uB_~|*upf!1eMt%ZjuXlx;@_;&PknPV_OKD~eCZc0L!uDEa|rsh;oE zE$H40vfsoz+HmFEpF=sl*(;nDiVXhL>$YRW;tgJWfFS zja#fNo431?2fUDYHATuU>z;o=F9;oyq8WBY8N7`2AkdEFUdJkyi+wTS)g>9@s)r-} zy|0r>ks%FY&{Zq2u&(HOO;H0*#R4k;Y2~bU#2DTq9$}T-*=j(7D~H z7+yCvNZ=qW>T({Co}mf?9zE9AjT~+l^L@E9pgJdZzq&4 z*jT5Kat>mH0LC44SztuW-dyUB*M|XZJ;ui~#F&@M&WbiBIg1$#C7biTZWV?*DsvVp~p zMeI_g^9JpSI#o^?>A*>bCnpXTkl;?591}_alCDRQ=fgW~Wmm{HO+=m1;&@NHSITe_ zL*?-yUkHkkb$hl(K4LgBpk1_oJtcmf&&R=?OG?Yw#rWp^E@$$*DMMM&wJN04)G<1@U6*1NznUV*V!R|2Im!up~NdGRmD`Brd(6 z3sp*PaB;FC4>4c3Rp79LDyD<*f~>qoss=35OuU+ENW7~Xz5ohhi&vYHLXI%NP;<11 z)S5V|g*pu4h?8aKKA6pc;;=5zQl0?3#8(w7BH!Pv(BQ3*|B< z!Ic5U5K`R0ouc zVsqK;5*ik zrxsGN+ZEr0o)Zi88g|K+cpzZf(zZ^?ER48b@(QuFdva|1j(ixka18&5~Q_Jl4%aYqy(CY z^eh0WX;%-F;KdCDp>cjgbq=zY9HH#XDmR3__%3OZr;!lo!cx^{oqjk5Jud*wg^~ub z;5AlB9k!#7dF^!&427Am7HPt};)S~0xbP|&28j}#$j0^Mkvz_E=BMbS;sRSNJgH_3Pywh~NOG-g14WpG1Folsncb zf!-|1HCY{sBw#rVwF>LQ_e?T=H?;eTG9$WQGLZ$RDg3}3YRk>p5TR+Wpy4*SOX3IV zt^12*tag+U!p_x}+EQ1_M5Iuq_?*=^1-4S5u=aSY$#-m8)A`Y_?rjAUFbiTYMBf#Ue2qCdR@yNvVCKQiI@j-Y_ znq0`@t`MCB!w-kl1@q0Vhq_1!rLI$2@SL&7^(oWf)6*)7pc(m$1DFc|Sux0Jx_IT2 zEO&9dns0ED^GYtPuE0@5F58SCki1UeT#tdl&}>lGNuABT?l>prEtZ)3xwgbUhI%7X zaY-#}Wg|c6!Rfu3U0rRzZvz*MLmSc-VLa6nJ%kAn*&={A56i6b+xnrt(%PZSw+e+2 zQMj`mewQM7c|^AW$n1NA7sSNSZCNUHJxY z9JIjOQP1ki4E^~O4i^8a2Bn?Dbb^*zizg;sxg|9$v$p~2CX&3$D2niHHHAkMCZxO7 z=Uuh9S8paU7p?+?MDVesm$%!AD`&W3K~`2t$kY-$v%(~kmW$v5TBCf(~4TM&Urpp7_y^jPm0 z{QSDYdD`aSc0F4o))A|;wW!&;5~j0ADkAoUCyy~JX0KL=Lqk9jj5C-Dy~lgCEZeO1 zgIk<)0fDc(=pcfN_m>u58>f*cCI-9q95b~k`qU3~HCs!$1 z=NMBN!W=~5A&IL|7mYH71s>|OyY@LlhDpVGWUC7IsBxbv_o{QhN?X^GEB7{RLlt(~ z6m|G-D{*EZoJ4s00Y-s1aP;6raDY^M9wZV)T}O=<(UPp{#0-hsd}3aUbl0|w@CE-u zPe~-m=ZgdIy_nC-M7pM3r;jsMRd7OE#=LboqLvO0k6;CmzBiSSP%Z)duKg{BEYBMs zoSfwE@4=wNJM1FIr_e^()02}u=Nop;h*bV?k4(G$gGX*{sLzd4x%gC4w@@QIgSlnr zTS;xo^?nPJ$PUV2B;g{Bix%gDJ*yj7s5f&wfXgWyjJ^cEYcxJH)^D*{z4nm!pG_PN zC1O+yHb}oIjY_yEfU9=kk1Kaq7Zl@Dd=K=sC=9nP%8$@mnB^${pcf{l_-@f6K8HyW zWl>5#=#>(J5Sy!PPyT=~>v#A2;pI$y;{Pl7!^_Ly_oMyr_cIUA9kN7YZ~rK#zfcf~ z_#6DZkG}l<-~UzXXa8{T$&;rCPoF-uK7Vyj{(88tzW?BtKV2W}?eG1-o5O=2cwfZg z`~Zm#R==+yB_MFCO0r4}_x>K*eDc+=K7H@_=-_~WfAGtLFZ`)5ed+SA4qyDtcRc#` zU-^iCzIL&~!5YYyKfeqA|DNCW1@)`=|Chh==xcmc^YL?4a0S5M2e|M4DFORED99dt zrT(=Q<%(8Nj~;y+{`x;i81*&#%3kKe5tbkK;Jy-s`%8&%e>oBEuO!0#kBM+UkqGxc zCBpsHM7aMs5$-1w;r?18++Rp)Fnzz-gA3D_CBiim;E2Ai2j|mA zaJ>XL;&b1F3-@D`2={&h9MSio2j};L=zHnGh3Wfe65;X$IHK>|gA3Dl;lYLJdz}b( zl>kTd-FR?e`o7`8h3UIZgnOF+NA&%39$c8d-|oSM>HFbCxZjxoNA&%O2N$OAM?JVO zeZMOa?q5lOBl`X|4=zmKzwW_>>H9Ym;r{IeIHK?0_29zv{d*o0lxNotCp=>9Dc z?n{Yq-yraE}w={;>o&8nNHx!TIx?;Qq-3II`Ei=D~&a z!9gP2lSH_GDiQ9tB*N7b;o6CC{Y1D?BHa6la33bZ{nkXd-mI!y52=_yYaEnB^ zRU+J{iE!Ucg!}D@a6g;~_d65eek2j@UrL1gmlNTBPa@p!O@#Zm65;-xM7ZCd2=@mQ z;r_!!xF1V|`y+{Pe=HI1PbR|s=|s5yA`$MtN`(7!iE#gIBHW)(g!_w$aDOEc?yn}o z{q;n+|1}ZrrxM}*_e8kAlL+_s6XE_rBHS1Mw={e5+Y{lwoCx=e65)PHBHZ^T!u`rb zxL*^3vyU4e{VLdDzYcJ}MZ%I@mOa}0Q|@`=qc1*sbm`)Ilx1I|Fj~}6KZNh{|Maw=!e?neMO+ZzY#EP2}jU?ll?95eFfhqfcZfiPM)t8aNhyA9|Fu; z!s&MTT(^KDzWnom`F#m+e-tqPoeLM}$KL?V&-*)~zh(XSF2MY18_w$o@&7jf<~O-; z&iMX%z&!!X4@fu~kI?x3EWUpeFrWTkg1*@JemCI$fP}$c`G?2NY4;bRT}${dvIr*xz;9b@2TL!2Kj(e&OE}aIt>;1mOO0zy+a(@Mw-yz|&{5kUSYk==x1V!!}l}5_eTNqAN;@N_znQ~mn97T z%0D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 z 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/bRo$Cao)rYm2nk7)`^ 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

CJs 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

A^GX=D5i3g`dZe@cW8v4I7ZkPE^PmjZo zs3-yR&j^MRTSn!RAEt zdCrt!N8B{_Jk2!i+f3O$-v?sxj&=HLy9CqOcIQIw4*3g6ZodI?8sx7ae*%f`|4nCU z{|@qD$lpWqQL$SfS-7`Dz7BZ@ByUsig5=|k^C9{QpF_R{xg83BCFI_as~`tJz5_WNvJrABKaTx39 zAa8`+8uA{=0X!}6i19

+@bciN%jIG!Ky+9pZ|mjQ7^&kuN=6XDfq zKpflg>NJP&d>Rl(bUdHtP&!xy#L+uHSnNy)jyr?$Jm`PC&Z%BHa$S z>ZRMsp>(i+q{FvzTz>dUjk9BntbJS_>8^@&_eVNL#>I(woS419fQY@88>lk9p4XAH?q*iBAW$XpOx+5ZK@_TTCHLv z73%PEjnVjt-)o{bVCaq?PpGeHcmKdvc;+1sm;xTyYQ~yx2HhLx98jtli zuQY9Db0yY!%o#}MHU3;gz4scVhT7%6qrZH?rmLZZ*05?oku}%0MJ@PhXC~l z3DG(@3TP+0vEWT?3j_(R11_8w@x*1{Gq<7db3h#pt&Vi81nIWI#jad-1(F9l0d1`U zQh?tdC}sN?2*i)ux3-V+eU7HkIv50<&@mAj5}^|#4?fWLg?XS)&I+nWVrviR!=O63 zbbn`TPkuO&K3t4J`4)~!X@i35iBqSHwpf|_&Q49l%6@oQkq8IBdU%LY8SRdb1hEJh zEaJf=G&+kI3!utGjA3xdmm2a@Wfr5sf}{fjk}$Dc7|d{x8S9jXVQ^)c9S+$S@(9R* zkViqD19=SOuON?ud&#+z+Q#G?)=-Fe6F_ zY`wfv^8-$@5T%0$fp}%*2ah7inxbnqIs^4iG{-gHQ>SkgXtp4x}?OasA$ zoIC%X?O++BLBf6~?28x~VTw+j$^1Dr^Q|+PKjYl_=uG%FkvX;V!_LGJ4KvoXn7S`t z)nPr0$cN~iJeuvrTZcf2)c(twza~lf5RZD7Ro5_EGguunSe-H8a_8_sXRK*8)n(_1 z6XP#dWULSKtn=!($+)%*U6;jnv@GMwm4zAW)}i&%nDx?(_3J3gxb^xKf+HR5sL4vW zLb%e)tVOu%MX_BkimV$?&Fpu53x7c@rv;guVzZ3)FCa}o6$7QMBrAY40o5N!W6>a> z?QH8YKphP6W~RNN6M?ohGz_Snp^-q_7#a=K)(~&)@(ndL7EQ#~fhb}2XG|oRy9AjD z9!}~e$0GXRstPDqAZMkoHWqQ;VdC*AH8B?X6C%ZxiOG!O_(hpRhU?g{LM05;gqLhjN$LoAio1S4)R#YiIBg5oCL|Ss~++J$f=Nv zAg4h-19>LoKOxV8U_w(A%6hL$^03Ryb-?;k~cUPL0$xTG32ix zXF<+`yae)2$OcG`96y5m8{}n>d_B@nAbG=ZIpofe=zi>m{Ax&EW1NiDm`S8DlSpGG zdlikDL>e>M0yJh4rGsaY3)@1zMSMhQyJcjn$PeDeRw6A&XxHa3~#=1F;?D+btteI`}bkI*%(HN@MC%Hyo#-Pn-~( zf&Yn{46a=4&vARfJ)Gi;joab!g_;G><6GwouyElzWIU&4LA$#yrp}GhSSo;b|7C*v zIAMo?pA_`_@f8d`X>2PVy!8Ofs9VW=xs>>QtV3XjiBTW-A$Z|wi>aOP3If~!v@j^yS& z>&6qqpUiFVhT6qRKixxhY^Yg;6LI_Dii^fa8wM}*GYhvXbZODpnQu}}F2q<>f|;{M zoUmRSj$s@_>RLOY#(mq}HMkSXAuROFx{Aia`7A=bEON)umte|qG=JGY6~!XF%Jqps z4ck*~njt4(`0XDXehWxMqOn+Nj))A&Lub~6Xw|A!szs|-c`aL|s7O(XhkUZF#NY@6JfOs-K*QTw7k?uQ@u3w}( z7-*OU2`2z)Z{wn^XGL4D0@6|aDMB22d6!=hECmvi^E!|Q(zk)cBZ>o$bs& z2NIK$k5bm)+7+m)ZQUKHiy`udI~nQ+)ZGw=<8Foq0u>rM9O&DI1_6D`5Qpf_hMF2? z>#(&yN{%--Of$Rt!)!-vPx2`A;SfN%AMBMjXqeq_9)$-PCZ32=6XWqFWJ(f4dy=@w z9@>+{Me;Z4ll3DibSKKmeMDxu6K&!ql-oODJ86s4E$?Reij!dG%8;}6Y|Qbg_&8(n z0!;scKO~w)jD;g2ELdP`>x8Q}OuQBtDiq?6!#Pt8=2m%~41nZiG7yr7PzyN_@(9Rb zkb@xEEgc0p74jI!S&+v;Fg}eoF7$k35Pln|2j)LSMd>Z6CkYgbO z9LqRJ9_x5W4i6I`_kx@RSqC{8lB7mGBtwfcAg_g-26-LiS&*CoJs-@y%V0khknEgn8aHvC?Q)Z8%Ic1`>eNTaX zOTK+^nD?#Vm%AvH~SV&hC>DVtx$6E)L%lfRN#4ZD^qa=#f zQ9j!Wk484Rb(DyPv|>`x&N|jna*fEQSx5OIX52T;I!dmC!AwYQ z9VH^zuey%%Ftl}7hS3^Bw|q0Vj&ifDqx?I1N{(2(7x?BarkwLd8=N{*bGI;G*R_=U zV?@oZrR3d7PxKqQr{lel?&)%CDGP!vXD#JaL_oTCz5qz~*t39CmwfM-w*E2N`edYg zHPXEWB#+M{T|VyI<+~k_?!XIybO+8Al)3}o6G(U9TtQi8ZL1HE?!XTR(jEATK&7^I zG*F45dZ1!M(}9W%T>|tSLstX!HuP(ty$#(4)XUHUpuG%n1!bk7Wf58qw4dp?g0in6 zuAuB=Xmy0Rg0k9lTtT_7A+Dh0tGQbTYk|rQ;g%z)GQ<^>`xxSistW1Q3x15h9_$WX zYKyI0rpP-@ruA7%NuO@8rR>+;-0*Mn%mEbV)>5KuvD$LWT1zO(>O;Cbx0cdd z9Li~j^;}DNGOmFQUrWjM$$6Q>@t0dm`ORHRS%=Y*Ybno!A03s zy0MXtYbmAUT1we*Ev0l^ODP@KQcAZv(%F)uW~ocw_&!#{=eHbn`A5Jk>hfN0$8&o< zu)Gi}AM1s@xU~iQmZ8?Ls>qnhkf!7m2EGkM=8m5X%FuHSgO_2pB=-Dv$YpBP z(~Qr!lZd?14l)B)v4lKH^DI$7R+$-IO&Rl78H`PI8$w zi<5qussjtU$R{qrW#Z}N=#aSU@JIOUY-h5xHhCI9E5~I<1a9Vp$z|4dYMSPZtsMHQ zT;`lFaG_diZXRHRsmwuWGNLlSYiJy@7nOM~kZx3d3Z&bU8-R4xrZz^RL_9LLhA|j&%JZ-I0-Q2+%Ne#`p#ukoI<7v~^Cjm1`z+R1Zh!6(BaPg5XUc z-AIwp(2dj@Al*p)7f3f!X_V+8cINGXbR$IqQ#VpvPc+cBRskJsh_vMZL!>B=FhnY# z))1+H!wqp2(P4&$0Uc_HtB7h0VRks{HfAce_J@8$s7(5hqM|&oJAkFld>g}q3=>a8 zDMw`*mC3t?FQGEIq|cS%`cRqwQ2DMmmC0*iL#WK-kw2--Nsy#6&x9nE`9nxjnWQjD zWpaffsZ3Irq%!{mNh4L6XYk@si4X1Cmtce;`R^c0j8ml}QSdR3=wPk;?1? zNh*`~5~MOoVUo(c8j@5d*9(%$qBM2*io585tW&HB@ABjl6xgg?v*egZga1M3Ewso3;OX2og%E?LGV4# z*6s1-Nb_>9gaOT;dnHU5P=1|W33D1o(;SVNqcL^=ntLToL9ppw2{X*vR&%d}8ERWc zVm{#ehWJXDlMI~$bfO`?66Sk`t^hj0&~-q^8@d^2h@tsF#~E4*G{O)GO(7DRrk9G^vF5^2thBemwdh(u#D8W4?1BpQ?PgJ?`5(U=^^MPm|)#^i`D8k0yg zCP#G9m_(v6IiickBod9u(OWbok!VcD0HQI8L}PNi7mZ0I8k5n5XiOr}n2Z5LV-ktR zWYi!UlSnkC>Wws}>Wws}bfht*BaJB?X-w%zV@gLFQ##U^(vilLjx?rpq%oxz zOzB8tN=F(~I?|ZZk;as6-83c+k2EGl?g`A0YRo->iOXSwY0GoaL_}MjZD`AX0+S7B z!=AvLYG>Zu6PWe3l}}(!F~n8JlMVF)I?E8L%rgz~3CwAR_ypz|hDdLoZiw{2Btx8> zN#3^b$vLuF8$w&shqNW-fbIa6HuG(Zqugv);Ve^NUkTfUk6b3Q+G3u-*dHo{BT@**9zY=+kwQ531q$Ja6vDC3PzXn) z5RSc!LO3GPmh3%5TM~)3WIrO>l1L#OdlZFmM4~O(w}`eR5^c#|Mzkf7XiJ@MEA$UK zU(%M+k+zhM{gQN~Eu~u@+7j`Kqb-SYv}I;lSdO;LeaV8$y>hfARuMXGGV|V*9Bugx zeE4z;{*uL27)^7uWsbJY(U##t;!Q_eUSe&lIofiLZ6$3v+Yo8XS%yekUTlc8P)HALD{h_vNTO-I`DN<*YAuP{W~Qi!zWWu_x-dAT9d zmOn5=+OokAY0DoQB5nB-L!>Q#B!t)?dH=t$wB^9=$kfv2x&JTC(UvIPEsnMvl%p+k zwB>%FD|276_-1{{;sT5`n)4#koEMSiycpAJ&WlKMUL0dJ=S8GBFOIgF^CHrm7sp-A zc@b&OizBe+yofaC#j#j(UPPiTIVy{`Bob}O@maJbk>1azqzxNhI2mBf4lyBGHx{y+vCRiMC`6Ali~hv?a%T z(UwG_Eg4;iwj>g5$rwPiC6Q=LMh&7Zi9}nf-bh=j-bh*vrF5h% zr6X-A9cfGHNLxxr+EO~wmeP^7l#aBebfhh%BW)=iX-nzWOTYjBh96J}S zP9YqTLO3FYaO?#Y!VxKiV}Ga+jz}RKdjN%SL<-^97bt`yQV7RBLm?cILOAv^3gL)E zTe9~MZAm2BlKqHiOCp7E>`@fL5s9{B-y+(QNVFw;8PS$RqAhj4q%C#6q%EZ*Z7Ci5 zCFw|8O1D0=C3`WwIF=|!Tjpp>Tm}3b0KY`Wm;A@yz0tU2)l!74o94x_vyoHo#j%L| zHsZyx1JKrUFOJ2#QRev{y_;(9b8Ub+e)%>u6nYPciiEY#QcS(9VP;os%rLb0GDY+* zUVXWuMPsW~jh}z<#j2;#QQlGep|*4~9rv-e-ukr4VV$hfGJ>@mbN^+J2JJj zdG7zqaq=y1>_{iJjl}_TSLx$_>;-gQ~_lDda@&L#kA&-LG3Gz6|u8@4qaCgXI zkbG%uJtSWmI~|fQjctJBOJlEx40azBINMWcbq9TQJgS&WlKMUPPMnVoa+!FCxu(ag5cR7m?(vh~5 zjUEu~vGZHXpB+L9tiTjprXjiD`< zqKSyM{F|XIpSENJ+A!Mk2|M%VXv@cKD{0HW8X|4^7ek~ipEX3<@)<*}3?f5s9{B z?;+ZfNVFyU5z&@J3gOtJD1;*tZOOhxv?Y;fOZGCNEr~>1>U>FC>U>FCN=MpKI`&J_ zk+zg>eP~O>&W^SuifGF%;Cq+h8}$qDwtT!4-+zNX-)9e8fG~^ z2&8>H52TNByc6jH!~@E&ON1CfNLLM{BRvX8$H%*vMV9mF5tR$=|Ia3T>|u~p{s#jG4yMomkr$pw9L=~pnn)5VfnTp5|%>Cf!;OU3ZQ=)B4N4O z5bIHhgykyJk+5tuM8fhVLnJKUG(^I3r6CfQ?-(LE`IdBO`^h|kXzy0=RJKt2z71LTX4 ztcO=1{{Zh&1oTaaZ$RM4I>F2&{Q8BF%eoEY`di zk*G_K%AzibL|t-x7IjIac`uICn)f0Sb;)Qz)FqLqOU4hPE{Q~4avT?RNhIo$Bf6+d zB2kwd(M4SniMr&7F6xp<)Fnr6QI|xbE*S%ex+D^H$?;y)C6TC0Mi-(ki9}s81`u^g zB>T->3C3X3!AySv07$SA~xgk=Q|20JFa;+g!m;W(D>hd!~qzFDX z#0pE^wrnhQc~p0I6$G34wxxf#AIR{^%e^QT;ch1s2=5Yd)aBQWy8IVfokBPwg>Xa) z;n)i*gddX;6v7dSx@7Mm>XJy* zCHoOkmqZHT*rO@;l(N8vvx7Ya_ewN4k0-?d|Mn>m`xyh6w!@XqcVH0wC?trvNXLm7<-7z)NA)U@j^*QM>$aF0(LQzt zQW^9B(h*cex+8&<-;ii4r(~o%JJMYm>3#~N{oV|u{c>_f9#2JEKLpa=a^JVyl;5{( zZ*8l&CobFB){(ecw>88kF1Ims4p3V|eBv_S&=o*wL)QUqZRlp8HiqT{ZDnXFP)9=~ zFoj59?qs^qPuh}gdvns3uj6n?TXJzNY0DNUc+!@8atw#eJ#qQX zd*ZSc8ouVdh&1O#q&Y9fw3_oG(wrB^Sj~A6Y0itIt>(OlH0Q-}S94xOn)BiatT`_t z&3SPw)|?lSXiJXDqAiI;TXK9BZAqj#FOJlj^CA*$$!I{dC6Q=L#t)(`i9}m+92ad# zB-)ZAx@b!x(Uu(1MOzYyw&aK|+LB1LB}Z@3mPDd083Ty6Bob}O@m{ngk!VXs7oshR zL|ZZj5N$~$+LBR&XiFl|mZ~?>mZ~>K%hHjyl#aBebfhh%BW)=iX-nxyTS`aTQaaL> z(lJJsjf*2#82qQsijM9BsKVv}G?e5z&@=8QSt` zOE#blqb+x}GjEQz>|t9;Tkc|rx9i;v^#j_|5NXRj43W0n-4JQZo`y(U?q-PeKsQ61 zgSI@jJ30hQoB6gSM_aDrMX|g~$kCQxH`=lgwW1J?NFf}NLOAw<3gL(p!m&S82uGw4 zjy-@vI3k5`>+rNFf}18HI2}qAl5bh_)mWZOMK_v?Y;3IQA$C;fO?A zvTqS>NhI2my^Lr}BGHyQU(%L3U(%M+k+zhM{gQONACzu=XiMCSIogsaM_cAz6pO1M z_oCQfPWxGd@5s@XGjp^hy2TuAxy93#2V*qd^iNyziHsa=84`p!+Oi;+i`zm`qqhNx z0A2zlGW9v2VI;$XcY*Y@Wm`<3>l*3;B#-Vuva5=8HIZ&2kUY)-QqC6wX}>>@w%#0V zT@vYDigd35$>ZOVt~HKLId22>Pf+-+gDya%gIfoMK<}7tSD?2LXBGq{Kz*!jHAh?S zYgQ*NLvmt-EyEB zL!>SH8zOBfMB4HI(~-73$Pj7E5<{dd`x+u`+0PJZ%L5IOw%kt$ZGThKmP5KDQ%jrY z{=YIuTcUKgINEX~F1Q?R`Q=Yr=4eaMn^%G6SfKOXq1j(l@?MX36(#L$-@^_GY+VUMpK5h9qB%ik24r2tL zw%i+%Pg@Rv@HOW}q&Y7l&3Q4V)tncR=Daw@YR-#Db6y;6HRna7IWLa8n)4#k zoEJx6&3O@N&WmHQ=DdhRTXIwuZAm2BlH;>zOCrsAairFq7m;X7MgyWPi9}m6eh_U* zB-)bWxM)iv(Uu(1MOzYyw&aK|+LB1LB}a78mPDd0IeLq>Bob}O7(lcok!VYf_o6L{ zL|Zbt5N$~$+LAGVXiFl|mW&!iTM~)3RK2kcsoqFiN=MpKI?|TXk+zhMw54>UEu|xE zDIIA`=}22jN7_<4(w5SZwv>*vrF5h%r6X-A9cfGH)=gX5Y+=m;n<_kaLECm0U5CzP zPR)XLcU{b=<%=E-6s6DBcbZdEfLETi$Lq`5dLL_nZcc|;gWqqcecxV(#<`}RG=?Ja zuCry-nb914wdMWB$5-(MHFE~9ZFm2Hc{A3unA&MhZ2^4RV+U=qvo=T0sap&bM;Ty+tSy`lE$rmQ>in@OH^afr=)0mBg^I0b|7uN4M`Vy>Q8qHt!&(3_tBGk_lgBrG{ z+B8E>7&>E7|9w~658n3|aJe!rOiNq{g^t(~*E)rGgP*n>f+ix`@;F0VK5fYcv|+U6 zk#^?I(U$yPVe5dj<#!E{wmiZRY0F~`k+vLch_vM~hDci;ZHTnxQHDql)EZ)iC2w0c z_G!x#y1OeP*vz*r{loo0hF2cgj_ECeZGyHPVjDN%&vrqpFlXF26jx*2prdXM z6v7cHgkvwG5ROQ+C3_FimPDd0*^h{}BvJ^+9z`J>k!VZyEut-nL|d|#5p78%+EV9B z+EV9B+EO~wmeR3bl8(0>(yb3|xi#8@p0*^)(UzIO#~UJTd8!ZwhUESK#y)L%Vs~U}Y4hCwSLbL;l+u(Uv*d z(q0mKET-i7lGw43d`awukbFt(m5_W%Y>u}4|L6ObC!yhM&WlKMUPPMnVoa+!FCxu( zag5cR7m?(vh~5jU zEu~vGZE3TGH4AL2Fh^TDE;2`3BBt74+VVm)5z&@23~l+eB^%I&(U#}hnKwsUo?}}{ zTb^x*wB=cbNL&8E5NXQ`43V~+Ziuwy`G!bao@a>kz?p_N2W|QN?z(Bg+uMf?A3tII znd2r*ol-b{__(?$6Nite>$%T9(*b6FzBT@**{!k$t zkwQ5301DxV6vDADPzXn)5RQF@LO3FYaO`Ci!V!tKWbYx`l1Q{A`w`KWL<-^9qbP(U z5^c%8MYJW6XiN4oqAiI;Tk3pCTk3pCTS`aTQabia(vh~5ZhdG=d|t`XmP9$)GDlnH zp0?x|oO{|5kDJbG$USYziy=o_Zt=9`FEN_tXv-XJspkuGwB@FxEw8Y))f{bkxostF z`4dB=EiW@f+VaPSNLyZNh_vO83|$A*V2HHkC5A{_&M`#V@+w25Erm#1{=#(2fqrg? zwB=kwq%DO=TmH;+q%E&CMA~w;A<~vt8X|3ZwIR}$*BByg`BNbb3>!*Y{sz;7(}VWm z>Rpa40jbVzP7dpCD*W3#_y5I3Io=Y5yT$RAoNCYUmj7GcGWl)G+)H9}Ph0Zamc#IF zf?Xi%A$NtG4%rj30dfz>>mm1oyb}^HXr6vIWN*lOAWI?n{he~iry%!%Okr)hx7o&3O@N&Wk7=EXP*HwE4jbhtk1&KpbQ9gO40a2cH9Rw9OCr(qW=> zuniE$-Ta_~L+PL^5J%wrpwOXouon==;{1T$S0zdZ{M-RY<@|szEhb6_hX8SW&JXzE z1EO>=1c)Owj@hAfFbas#Kz?wVL+RjjAjS{*!C4NagA0H-j^_t69ZCm30pf_BANsffKUm^WI(QCZ6J>K`9Y&Y z>0k{Iql^4ttwZUc4QALF1LOy79ZCnCfEYF82VEUX2iE|p-hSawI(RtJJr?O!N4obT z-HwP957T8o_ zj<(FvmK*W3;)CV5h;XYf2a_SNFf}10EKWw3gOrnD1;+Y2**A{AsmrHIQB9M;fT^f1!{)9N4^Df zMCssQ=-7|s2ZuSN5RN^HLO7yyFa}%Mx8w)o97+dg0HLa&DiE^}Mj<(FvmS_k$+LF9&j<%d$4skZ(YCfNT%?up{K& zklRBZ0J$UNQII=99tYVKl3zppU(=R%qTOrGi%4@`M4IzrOshFBBF%YmjMbbMk>UEu|xEDIIA`=}22jN7_<4(w5SZwv>*vrF5h%rCT>`X|shj z3v8+|M_cA-%Z;Edm!XM>w)}^oEuXez1KKd!^6z%$&C!-iZ7XTZzZoKJ`K%$*mMi;fNH%5h;XYFQ^cXNFf~iLxpfe3gOrTD1;+Y2*q7aTqv?co%(UwG_E!oS6wj>g5sq-amsq-amDIIA` z>DVtxN7_=l^`R|UvZ5`CaQN7$R-?h9T0H?-?R(DMZ@x z6Vs8l{MZm_%YPXnZ7D?B@&nV6w*1HtY0K9Qk+%G&A<~xb8zOD_p&`F52GbA3bY%*2Lf-`1@-?)3&3O@N&WlKM zUW{oq=S8GBFOIRA^CHrm7e`yoc@b&Oi{q~5yofaC#SvI@UPPMn;#jOXFCx*F9F;{| z5{b6t_$=CzNON8ssWs(vh~5jDs!VxKiBT@**UQi(%kwQ53hYI0{ z6vD9wPzXn)5RQF;LO3FYaO^V_!VxKiOPP)@%pekN$=*Y>C6Q=L_9LP#i4?-IM^Okz zB-)aFi)c$C(U$CGL|YPxw$%BOw$%BOwv>*vrF86spafzN@{>d!3PZNH#vupPfS+eBiA)Xsgc_QFEk zyB{!lRL}xPz~_^TVh%i?t5GvNz8+5h{UDgm-$NmJ%)=lVacxr=UP(l{l8Dm5GPgB9 zSniO$HsdRo;#B0)ZylvLp~+I5I)2KSQRC}I7LFNTzy8IuL#J5rV80tvLfaLFB}AkW zB1#ARzPEG>9NM@NIuE%VvW^m(m?)uBCQLqK_~eo9N;_!6`1;{v#@9_AI&t!ZX=kqQ zl{B=QS5)g$Qb)LJ>AUV)8sx5}9Z@Rkm55YQMCo8*fK|JuTjY?H)NQGz8{}3nVhS5( zvO!*fZ37YoMIT<^t?+jh&PhoPa>B&A$!u!BV$1KX}0*E4-$Q zj#)A_*O#4-ZHFd`jy^m@{!L0;V?+DbO{t$e;mnP@$m-Ce4joiK*}5xhjk{xCVdDN- zOf4Y0I^}-u8tdn-t!{;3@erwah;(g9$7@SE_4b42)V4ixPHn+T=-tb0+oSt3wdrOo zYBOhW+oR^xw1+M9hF-FE@H zU}vI=NOx$YJ1NpligafKJ%h3+uu4o0Ru^C^Q#eQ@+s41JZC0X!>BGvMhQA#$72M3H z6vow!95Z};X`hKxM~w-;#K5mG6z$sN`9ra#(6IfL8Jc&E**!MNVq&S^7eP)l56v)arW)Ut7D)9tnx zyJh0;nUdAYv1VwZTIs`T{SN-l_|j?}F=67FLr)$);xvvKQ<|vV4XLb?>qdVligX*-?%zW`7bY%{c)S1hmtG)Wq1{iN zICcyI*}DH(lfNR~P(uGEp4vNsyrDN-t4g<_R+^co`qa!q9 zQOjru;0U~5A@2~j2Kv)B?9jIb+Sha)fhvFs0$eTY=A zWu9{L1LzLe8c)d*y$l=2B}$Y&EKy{?Atj2IF>OqJ;Yfy1Q%4{G{p#hak%8BaB5Wd+ z7*QI>rNmNeZGpoFP*mZsBG{(Ga$athbXPh6Usg_0&s`&N`TQd~2cq@Q zCu%4zpSPoYh}J)!sIjeSOk3RgMA8&(tj~O}P^xp?xe8KUf-zjy&>w6b5 zo^YDpBPZ95s7GguL~UM*qmQ3@kE}a+>Zl$^Q`-W6d8szIK6nP1)O+&C;q}Ayo8weo ziqS_V(T^$A(xyca_u`hk6pta3La%D4qK)sxo2%x$zVEzf<9l)bu{p2l&Wkp_7bjDi z^ZJ$ZqK)sxyUXUh{^Go7<9jh;YtHK(=S3Uei=%LJUYO>wL>u3WkLxz))zf*=#`ofV zVsl=6%OMkOd@sf?&3T>TylCTlF=}nji?6O`qK)sxDUjy8e(Stw<9l(SY|iUR=S3Ue zi{Wo`*ZeByMH}CX9d~nH-*Oi}ZG10u#LaqDJ1^S!UTm+;?dw?QMH}A>9aFPjXE-m~ z_+A#RHq!%K?YwB?dttV!*?ryTylCTlq5E#uYnk(+jqlZY9bR8JFWUHC2tlzgSDnF6 zi?%w07D}Djr6JMA_d+n$tXE&>MH}CX^N`K$YpC<0jqk;Kx#qlPI4|1xUJP`b^Wvve zm}ujBeH)t06*Xt^*1w~L(v!}MHojLOG#RhDQ48j0ytrV8sStm@S2t*yJHnoBP@#?Q z)qNdaM>#Lr_+GoL!|N>PMH}C%$2z=z<-BO)dohk`?!2CMUbOMOcJsXO11T)|Pn;KR ze6OC+G`FvvI7lMV#`oGCnv551GW*)!dC|uA+5?*Ayy~16ZG5jiJukm~&30b2@xAt1 zhu0sR7j1m6UhD9B(RtCv_u6|MUM(3iAkoJ6>J3eE*Zi)|i#EO&XCX6Qe)}5cylCTl z6`5DlebjX4MH}C%*xT1VRnPyN7E1G+7j1m663@#&!pEH#ZG5j%&+E9WPQEm=uMeFU zZG5jX^9s)kKaj#B?8>kUi8j7hIW(Cv{N-_-2W7l!oEL3;uL{r0Kf+U;7j1m6O3!QA z(9?F!?CUb;MH}C%%DkFBuLaJFHog}l;^x}do6d_izSlnHjdXT8{ez1d4;9n_W(1U7j1m6{k(noSMM#(i#ER3{+^eA z^*-agXybeJ^Su0P<5TBF8{g{y^9s)kKaj#Q?8ZPIi8j91fzV{i@V(vdcqHR>i1VV2 z?{$#p<)7C$=S3UetH0;vpV#Hii#EPjjd_LV<=5ArofmC3z#@x2B@lPN>LRer~L(Z=^W#Js}u!VjeI2#y>ufIAk+W20z<`tfoUtb?PFWUHCM|k^Ma`0KW&co8! zIUgI5Xybc*7n)3I%>Hhhb2DBCI4|1xUg)4>Ua5;x&u6?wIWOAyUPqc&cwYE{6dvJa z&Wkp_*HO@9j&Q#}lw6nby2p9Z#`ikf^YYK@W#>g3-|HC9%RjHYw%CY78{ca%G?{+h zudltG7j1m6W6dk<=l%K`?7V2>dmZO_`R8?}^P-LKg{B)TL;t*f>AYy;dmV3HP1n~F z=S3Ue>jZCKetmu5ylCTleb4jq&ujZ_un~zizSoJ+WUdYWy!LfowDG-8GOzHw{Q9bM zUbOMOzVGep)0G3~(L(7*&Wkp_*HF)^;Fs+`%Xr=HylCTl4fDJfFFgCzjMq!fi#ER3 zaPtb!3qO#;BW$&;e$&SHIvJWw8TK7gaBaq`xAUTn?=`~nnzZKT12bMnJ1^S!UL!rP zTW)%6RL1Kp=S3UetIqS9c>XO{X1s26UbOMOPVv0_zITc9qK)r`1B#V~KbpViylCTl zjrP3uz3cdYW{$9PJ8VRvjqf!EnoP;R{{HbZGhPQdFWUHCr+QwG?SIr48LtV>i#ER3 zX`a{YPK_UAyng1qXybd0^}H@`^YLRDuSc90ZG5kBo|hjvta4to@x8`-UY`}+xH7Y^ zcI~kdi8j911ZXm4xNP3mTV=eeofmC0mylCTlP4c{MtgMA)*uE}x zUbOMOCVO6=pY^?1E#Kq3XybcL@x1(!f75x<#`miCy!?{ix&t;M(Z=_h3QeXA{gVHV z^P-LKb-L%}m;CY0i#ER38J?G4@-v(lZG5k3o|j+pe{f#3@x9LUy!?`X(RtCv_d3hG zn(0Ni!$u_9_+Dp2(_9Qt?!0K@d!1umVGMvDNZ}PV(s|Lw_dj&%bI@Wp7#`l`xdHM1Dxz39=zSo88@cOm$qK)r`1|K`GlJ{S!q=nL7ofmCqF;78{g|9&&wb6Iu~Fg5^a31i=oMsyzg~@^P-LKHOuqz@94)kFWUHCvpp|=g#EGe zqK)r0$Mf>z!@HdqZG5jwJTJecyyCoQ<9juDUVcmYuk)gf@AV_k%fF&_+8!H`Xybcb z3QeZu{nFUSdC|uA`myKb-@_j7ylCTlUFLcDk@-~TMH}DiC!Uud_g?9|Xybcb?s?${ zQh3eZ=DcX*dtCudrZg%~eF)uf==HesqK)r$rRU|>a-;L2jqmkS&&#jnf*r6Ci8j91 zRnTOP(68nFoEL3;ud6*Tzm`vRUbOMOe&%`kwS1-XqK)r$jpyap@`KKcHog~v{@8i> zcL9ygi#ER3T+hpod$-#W8e_n?@*gKPl~*J+)mJApRmImOHD&XXn(_rnO~nddQ(0Wrwv~5YmBr;{ z2~BBb|AeNzWRS0^Dk1Nsj0X+p(!t0l+={3PG~Btc5m(HTvsA{+9nr9oV zC@wDPnb4G1{yO1RQ93WFslGX(sVuuKsVSfDYtRQAax=1NljUst-ZZhmsJ-gH04DDlbZ5jNlnGY2~Ab?bqP&%@y$t1 z>B6L@e0fq+xi+b(EmA7f@?Y+3Xs(n&ZUFB;^imFB?G^N$& zBsAs4e3mtKUL}>q7bi6(weBLcz55Xw78^SQd4?NJm=D)vZAWdaT{u?f1J=%RNS1@R4(;3 z=-J*+YN|W6j~#PqQF&=qRYFr%J}{xFs5mC6sT`ZsR9%tKRF(fNUWTPbl@&#ceVfXP z;`bAp%JP;S;zw9oR$5$?)RYWKXsS!*CN-tOc3wW^#l`)Tnv!`5O=;2Uq^7v2qn}S@ zDIN{a)O%@Zd1cumfA1AVm2-WYilVBqUe4u3l_iTj8)!-kd`)p#(dwk8xBwBcuGQXn zgs>iE%yb|A@>0B2W?Wr3As*4ISj?CEbtl{ zsZ2w7l(XR~VBRUzkLYns?FroSXZ-2@0wJ%ZOwPldm)Y1he3-?hD4#==&;8Ihmk&PI zV##a_{ld>DRvHgjKI<-xg=P~kjR&IAxW(oCfJ)<($#r$MNB*;8j_vC~%clhYS*kZr z-+j;Ql7Gl--gh<-!GqDhUU2(*Q2WBuVPosY(*%k8V*0GvGkq9a^HKxwpL_rC*Zn@r z-uoi6X_?B@`@_-RTkedFNDoJQA2DvCRHlhvV~;?emm2K09@_Sl;_Q7rYBtAUUkH6H zJreD!r`y*fSvfyuUhUzSg>#pc7QJ zA9P;!F+XiKBe83AYL=dga=y^z{8T3A9@EU`8T`*n^}>Ia^WqMNb<23Ia9+>AE06EO zuAs{c__O1orivPtR-~z>hF7jM{B`WN`Cm!xVhl7qO6ANge~)YmUEAR+XqBkxJ&jWp zI_PJi)-f};)vtYeQ|6dk7U~s4W@GM?pkc~rUM(~Q{#njbENfH!4gd2}=cKfK;HP`M zD->@{OU(u$x}|w*p1L}_F8*#d@qS}zR2QEV%5!O^E~E&aGq3Q9J9gz>qq1{;-fULm ze`xnygj#n~M$cvC%sR_UO~Zed*l&LO$bs28zi2i?aPDEwFGQ%o<@`cc&M%qQY531{ z<;O$NC2$|{cKQ$e&P%n8<@{2V^T{sfm$Gt3vxL_r_|N>tcIC?%kcF2sTSQ)JA=F_H zuq?{?PM7nttejslFP<-3)0no`_RBuzSIvg+$_!i6D^bq7bjL=dS28)f*0da2zcppN z&URkQ;gxAkyqfHIqSnOzGSXDzN3bx`@YkEjhsUIR;_c!!X#IAP+4~{8s9l&1vk7j6 zhDnX0rzW!|QNGgidg-E3$Ha%e*T2AAj>tx&OHAoZmK^PS|01wY(MOe1XgP zt*o3^n%5gVOr+vt4?aCR=T+E}m%0!(;W4j_&;u^#m03BzV_tv8e|qhb`pKZ|oEy#N z*Rh=6iE?haD>fp%la(|3hrHAk_}>zL|9NcD%

1X*L{*aO-Yqb(HhrF6Y&j^U!g1 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�a62B(oTR`Ec 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 RW_T(-8$ZncJ6-Mk>wjEp$#4Jw literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/lib/pugixml_sd.pdb b/LifeGraphics/pugixml/lib/pugixml_sd.pdb new file mode 100644 index 0000000000000000000000000000000000000000..38170c9a30a43d70abbd9dbf77848a38a6fa6025 GIT binary patch literal 274432 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 C*km~X literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/readme.txt b/LifeGraphics/pugixml/readme.txt new file mode 100644 index 0000000..9bf4d5f --- /dev/null +++ b/LifeGraphics/pugixml/readme.txt @@ -0,0 +1,52 @@ +pugixml 1.0 - an XML processing library + +Copyright (C) 2006-2010, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) +Report bugs and download new versions at http://pugixml.org/ + +This is the distribution of pugixml, which is a C++ XML processing library, +which consists of a DOM-like interface with rich traversal/modification +capabilities, an extremely fast XML parser which constructs the DOM tree from +an XML file/buffer, and an XPath 1.0 implementation for complex data-driven +tree queries. Full Unicode support is also available, with Unicode interface +variants and conversions between different Unicode encodings (which happen +automatically during parsing/saving). + +The distribution contains the following folders: + + contrib/ - various contributions to pugixml + + docs/ - documentation + docs/samples - pugixml usage examples + docs/quickstart.html - quick start guide + docs/manual.html - complete manual + + scripts/ - project files for IDE/build systems + + src/ - header and source files + + readme.txt - this file. + +This library is distributed under the MIT License: + +Copyright (c) 2006-2010 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/LifeGraphics/pugixml/scripts/CMakeLists.txt b/LifeGraphics/pugixml/scripts/CMakeLists.txt new file mode 100644 index 0000000..6c748d6 --- /dev/null +++ b/LifeGraphics/pugixml/scripts/CMakeLists.txt @@ -0,0 +1,5 @@ +project(pugixml) + +set(SOURCES ../src/pugixml.hpp ../src/pugiconfig.hpp ../src/pugixml.cpp) + +add_library(pugixml STATIC ${SOURCES}) diff --git a/LifeGraphics/pugixml/scripts/premake4.lua b/LifeGraphics/pugixml/scripts/premake4.lua new file mode 100644 index 0000000..3584637 --- /dev/null +++ b/LifeGraphics/pugixml/scripts/premake4.lua @@ -0,0 +1,86 @@ +-- Reset RNG seed to get consistent results across runs (i.e. XCode) +math.randomseed(12345) + +local static = _ARGS[1] == 'static' +local action = premake.action.current() + +if string.startswith(_ACTION, "vs") then + -- We need debugging symbols for all configurations, but runtime library depends on official Symbols flag, so hack it + function premake.vs200x_vcproj_symbols(cfg) + return 3 + end + + if action then + -- Disable solution generation + function action.onsolution(sln) + sln.vstudio_configs = premake.vstudio_buildconfigs(sln) + end + + -- Rename output file + function action.onproject(prj) + premake.generate(prj, "%%_" .. _ACTION .. (static and "_static" or "") .. ".vcproj", premake.vs200x_vcproj) + end + end +elseif _ACTION == "codeblocks" then + action.onsolution = nil + + function action.onproject(prj) + premake.generate(prj, "%%_" .. _ACTION .. ".cbp", premake.codeblocks_cbp) + end +elseif _ACTION == "codelite" then + action.onsolution = nil + + function action.onproject(prj) + premake.generate(prj, "%%_" .. _ACTION .. ".project", premake.codelite_project) + end +end + +solution "pugixml" + objdir(_ACTION) + targetdir(_ACTION) + +if string.startswith(_ACTION, "vs") then + if _ACTION ~= "vs2002" and _ACTION ~= "vs2003" then + platforms { "x32", "x64" } + + configuration "x32" targetdir(_ACTION .. "/x32") + configuration "x64" targetdir(_ACTION .. "/x64") + end + + configurations { "Debug", "Release" } + + if static then + configuration "Debug" targetsuffix "_sd" + configuration "Release" targetsuffix "_s" + else + configuration "Debug" targetsuffix "_d" + end +else + if _ACTION == "xcode3" then + platforms "universal" + end + + configurations { "Debug", "Release" } + + configuration "Debug" targetsuffix "_d" +end + +project "pugixml" + kind "StaticLib" + language "C++" + files { "../src/pugixml.hpp", "../src/pugiconfig.hpp", "../src/pugixml.cpp" } + flags { "NoPCH", "NoMinimalRebuild" } + uuid "89A1E353-E2DC-495C-B403-742BE206ACED" + +configuration "Debug" + defines { "_DEBUG" } + flags { "Symbols" } + +configuration "Release" + defines { "NDEBUG" } + flags { "Optimize" } + +if static then + configuration "*" + flags { "StaticRuntime" } +end diff --git a/LifeGraphics/pugixml/scripts/pugixml.xcodeproj/project.pbxproj b/LifeGraphics/pugixml/scripts/pugixml.xcodeproj/project.pbxproj new file mode 100644 index 0000000..2e99f4b --- /dev/null +++ b/LifeGraphics/pugixml/scripts/pugixml.xcodeproj/project.pbxproj @@ -0,0 +1,213 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 45; + objects = { + +/* Begin PBXBuildFile section */ + 5314084032B5001276189718 /* pugixml.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0E0405306C15001276189718 /* pugixml.cpp */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 0A4C28F55399001276189718 /* pugiconfig.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = "pugiconfig.hpp"; path = "pugiconfig.hpp"; sourceTree = ""; }; + 0E0405306C15001276189718 /* pugixml.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "pugixml.cpp"; path = "pugixml.cpp"; sourceTree = ""; }; + 7C1A11945858001276189718 /* pugixml.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = "pugixml.hpp"; path = "pugixml.hpp"; sourceTree = ""; }; + 1DA04ADC64C3001276189718 /* libpugixml_d.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "libpugixml_d.a"; path = "libpugixml_d.a"; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 47481C4F0E03001276189718 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 65DB0F6D27EA001276189718 /* pugixml */ = { + isa = PBXGroup; + children = ( + 20852B6E3BB4001276189718 /* src */, + 578963B4309E001276189718 /* Products */, + ); + name = pugixml; + sourceTree = ""; + }; + 20852B6E3BB4001276189718 /* src */ = { + isa = PBXGroup; + children = ( + 0A4C28F55399001276189718 /* pugiconfig.hpp */, + 0E0405306C15001276189718 /* pugixml.cpp */, + 7C1A11945858001276189718 /* pugixml.hpp */, + ); + name = src; + path = ../src; + sourceTree = ""; + }; + 578963B4309E001276189718 /* Products */ = { + isa = PBXGroup; + children = ( + 1DA04ADC64C3001276189718 /* libpugixml_d.a */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 566D16C45B6D001276189718 /* pugixml */ = { + isa = PBXNativeTarget; + buildConfigurationList = 714F05E01D71001276189718 /* Build configuration list for PBXNativeTarget "pugixml" */; + buildPhases = ( + 0B66463C5F89001276189718 /* Resources */, + 6E6449051D38001276189718 /* Sources */, + 47481C4F0E03001276189718 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = pugixml; + productName = pugixml; + productReference = 1DA04ADC64C3001276189718 /* libpugixml_d.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 08FB7793FE84155DC02AAC07 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "pugixml" */; + compatibilityVersion = "Xcode 3.1"; + hasScannedForEncodings = 1; + mainGroup = 65DB0F6D27EA001276189718 /* pugixml */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 566D16C45B6D001276189718 /* libpugixml_d.a */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 0B66463C5F89001276189718 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 6E6449051D38001276189718 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5314084032B5001276189718 /* pugixml.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 6B5515257190001276189718 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CONFIGURATION_BUILD_DIR = xcode3; + GCC_DYNAMIC_NO_PIC = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/lib; + PRODUCT_NAME = "pugixml_d"; + }; + name = "Debug"; + }; + 4FDB54E4253E001276189718 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + CONFIGURATION_BUILD_DIR = xcode3; + GCC_DYNAMIC_NO_PIC = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/lib; + PRODUCT_NAME = "pugixml"; + }; + name = "Release"; + }; + 5B6C3A6F39D0001276189718 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + CONFIGURATION_BUILD_DIR = "$(SYMROOT)"; + CONFIGURATION_TEMP_DIR = "$(OBJROOT)"; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "_DEBUG", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + OBJROOT = "xcode3/Universal/Debug"; + ONLY_ACTIVE_ARCH = NO; + PREBINDING = NO; + SYMROOT = "xcode3"; + }; + name = "Debug"; + }; + 36FC55CE27E8001276189718 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + CONFIGURATION_BUILD_DIR = "$(SYMROOT)"; + CONFIGURATION_TEMP_DIR = "$(OBJROOT)"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = ( + "NDEBUG", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + OBJROOT = "xcode3/Universal/Release"; + ONLY_ACTIVE_ARCH = NO; + PREBINDING = NO; + SYMROOT = "xcode3"; + }; + name = "Release"; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 714F05E01D71001276189718 /* Build configuration list for PBXNativeTarget "libpugixml_d.a" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6B5515257190001276189718 /* Debug */, + 4FDB54E4253E001276189718 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = "Debug"; + }; + 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "pugixml" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5B6C3A6F39D0001276189718 /* Debug */, + 36FC55CE27E8001276189718 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = "Debug"; + }; +/* End XCConfigurationList section */ + + }; + rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; +} diff --git a/LifeGraphics/pugixml/scripts/pugixml_codeblocks.cbp b/LifeGraphics/pugixml/scripts/pugixml_codeblocks.cbp new file mode 100644 index 0000000..64a5320 --- /dev/null +++ b/LifeGraphics/pugixml/scripts/pugixml_codeblocks.cbp @@ -0,0 +1,44 @@ + + + + + + + diff --git a/LifeGraphics/pugixml/scripts/pugixml_codelite.project b/LifeGraphics/pugixml/scripts/pugixml_codelite.project new file mode 100644 index 0000000..0c242c8 --- /dev/null +++ b/LifeGraphics/pugixml/scripts/pugixml_codelite.project @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + None + + + + + + + + + + + + + + + + + + + + + None + + + + + + + + + + + + + diff --git a/LifeGraphics/pugixml/scripts/pugixml_vs2005.vcproj b/LifeGraphics/pugixml/scripts/pugixml_vs2005.vcproj new file mode 100644 index 0000000..27616ea --- /dev/null +++ b/LifeGraphics/pugixml/scripts/pugixml_vs2005.vcproj @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LifeGraphics/pugixml/scripts/pugixml_vs2005_static.vcproj b/LifeGraphics/pugixml/scripts/pugixml_vs2005_static.vcproj new file mode 100644 index 0000000..c0550a5 --- /dev/null +++ b/LifeGraphics/pugixml/scripts/pugixml_vs2005_static.vcproj @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LifeGraphics/pugixml/scripts/pugixml_vs2008.vcproj b/LifeGraphics/pugixml/scripts/pugixml_vs2008.vcproj new file mode 100644 index 0000000..c0215f1 --- /dev/null +++ b/LifeGraphics/pugixml/scripts/pugixml_vs2008.vcproj @@ -0,0 +1,339 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LifeGraphics/pugixml/scripts/pugixml_vs2008_static.vcproj b/LifeGraphics/pugixml/scripts/pugixml_vs2008_static.vcproj new file mode 100644 index 0000000..3ae1798 --- /dev/null +++ b/LifeGraphics/pugixml/scripts/pugixml_vs2008_static.vcproj @@ -0,0 +1,339 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LifeGraphics/pugixml/scripts/pugixml_vs2010.vcxproj b/LifeGraphics/pugixml/scripts/pugixml_vs2010.vcxproj new file mode 100644 index 0000000..380dc85 --- /dev/null +++ b/LifeGraphics/pugixml/scripts/pugixml_vs2010.vcxproj @@ -0,0 +1,161 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + pugixml + {89A1E353-E2DC-495C-B403-742BE206ACED} + pugixml + Win32Proj + + + + StaticLibrary + MultiByte + + + StaticLibrary + MultiByte + + + StaticLibrary + MultiByte + + + StaticLibrary + MultiByte + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + vs2010\x32\ + vs2010\x32\Debug\ + vs2010\x64\ + vs2010\x64\Debug\ + vs2010\x32\ + vs2010\x32\Release\ + vs2010\x64\ + vs2010\x64\Release\ + $(ProjectName)_d + $(ProjectName)_d + + + + Disabled + _DEBUG;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + true + + + Level3 + $(OutDir)pugixml_d.pdb + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + + + + + + X64 + + + Disabled + _DEBUG;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + true + + + Level3 + $(OutDir)pugixml_d.pdb + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + + + + + + Full + NDEBUG;%(PreprocessorDefinitions) + true + MultiThreadedDLL + true + + + Level3 + $(OutDir)pugixml.pdb + ProgramDatabase + + + NDEBUG;%(PreprocessorDefinitions) + + + + + + X64 + + + Full + NDEBUG;%(PreprocessorDefinitions) + true + MultiThreadedDLL + true + + + Level3 + $(OutDir)pugixml.pdb + ProgramDatabase + + + NDEBUG;%(PreprocessorDefinitions) + + + + + + + + + + + + + + diff --git a/LifeGraphics/pugixml/scripts/pugixml_vs2010_static.sln b/LifeGraphics/pugixml/scripts/pugixml_vs2010_static.sln new file mode 100644 index 0000000..4364256 --- /dev/null +++ b/LifeGraphics/pugixml/scripts/pugixml_vs2010_static.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pugixml", "pugixml_vs2010_static.vcxproj", "{89A1E353-E2DC-495C-B403-742BE206ACED}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {89A1E353-E2DC-495C-B403-742BE206ACED}.Debug|Win32.ActiveCfg = Debug|Win32 + {89A1E353-E2DC-495C-B403-742BE206ACED}.Debug|Win32.Build.0 = Debug|Win32 + {89A1E353-E2DC-495C-B403-742BE206ACED}.Debug|x64.ActiveCfg = Debug|x64 + {89A1E353-E2DC-495C-B403-742BE206ACED}.Debug|x64.Build.0 = Debug|x64 + {89A1E353-E2DC-495C-B403-742BE206ACED}.Release|Win32.ActiveCfg = Release|Win32 + {89A1E353-E2DC-495C-B403-742BE206ACED}.Release|Win32.Build.0 = Release|Win32 + {89A1E353-E2DC-495C-B403-742BE206ACED}.Release|x64.ActiveCfg = Release|x64 + {89A1E353-E2DC-495C-B403-742BE206ACED}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/LifeGraphics/pugixml/scripts/pugixml_vs2010_static.suo b/LifeGraphics/pugixml/scripts/pugixml_vs2010_static.suo new file mode 100644 index 0000000000000000000000000000000000000000..75d8d3480ac1e5935ed99229ccf1a4e7f5c914d2 GIT binary patch literal 9728 zcmeHMO>9&}9G?OO3Me2SDr#N%5VU38wv>-THSKmQ#5P^orEsW{{dij+ecf&Ly@gig z;zd1ROg!kt0|{OXq9n#dPRhlYs3j&Q#&{5nQ4dBr=|QN!-@G^N+edfz?Y2Tl_;3C@ zGjHb2f95~`&v}<7SO0YS{D#ZI15b*D;`&sBSnPUUhdNQhDj^o2!uIv4sVV!R5UAdP z+lT|#u+~M+8P=oZc`pGr0G9%n1D65s0ZJG{9g|% zU__c8G42A^h(V+wtFudI+@^{CZG3I=#vmIFtZt(_}itg1Y z>5eiL+Dz&k!5{s&=oS4U4BBJDv|n`ocY;23vk~Z*KJB)mPu=yRvH?6#+5zf6?Ev+k zw7lc*!Lv2MwZL`2^}u_9U)9RNN8+z8wR+zi|Td=$79xDEIi@Nr-h zkne6s8FcC)lr6wkr_KI$S6$IkCA#l`hXac*z47%oA3WE8;rqJ2-^ac<1@=`1k#H

mAqK>d08 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 z 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Ü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 literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/scripts/vs2010/x32/pugixml_s.pdb b/LifeGraphics/pugixml/scripts/vs2010/x32/pugixml_s.pdb new file mode 100644 index 0000000000000000000000000000000000000000..93cf114820dfd62de67b8579e11eaecac8b009b9 GIT binary patch literal 266240 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 b1qc)%P=G)I0tE;ZAW(on0RsP*Bk=zL#Gi@b literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/scripts/vs2010/x32/pugixml_sd.lib b/LifeGraphics/pugixml/scripts/vs2010/x32/pugixml_sd.lib new file mode 100644 index 0000000000000000000000000000000000000000..14742adb28ebfcb0ee4a606c15413530aa59937c GIT binary patch literal 1318866 zcmeFa>2h4Tt|nUD5&h>pMc)YjZ;dW1;>Oj=c^;}S7%)GzyITxU;fAWFaQ1j-2MFjPagb~fuAz)QwDy@z)uo>FIa(z47tvA_+ys=*GW{cH) zlRs_NU%zGB-CdT6pTE^wS*`x34d2X8v+V!>g)!wHUxfvMuPUOvs*dvaaW?+HIA^7ZEO*PF94aPLRP+9Kx4>mUR!*E--NKuf zu{Ae*1#Eds8a}THL7!(@yr#8g5C0ePWXcQY!C$p$Ed)5pfat^NiJT7xmyytEE&m80 zeBdcVb8VY{nJqJCPKBtgIT#k6WgpJZnOv*?hVd0M)T(dTR|Nr}1%}c3%RH;qdWt(V!Fu$!Q~XC>|GN8z^)r9Z zDw>C*>C9v)a_*!fYzNdm0VRK&>gj#Eetep3=2%6t4~dm56AQ--60%y~&Ap|gf#n&~ z{sh{)R{tPCZ(%^pzwWS@>rdO=?DlW*K{un;dYgs!hcNa!SsB-x?9Xv`SyAClyw3&V;$>E z>m8{_6i@wny;waAOi$N)`j+2dc0SKGcU#y5(kkP*{*aj5@=$w!3f=Bv(lym^S8I>DlW$?TxP9^dj5esN9@ygk z@dn1u3)&ME4{I%M*Sn2JdnYTZ*6tUpyL`685_GeNwfA8)dz^2dVDjbr%>o<3`P~AG zX)s5>J}$-TAS4F6pKFK+J{nY~m67qfc)#9|M3yc7Y^?@cR$AqZA+AuR7t_CLgvTo< zB!6maK8i$H1oqPIU;#i9&CP7PxK$gn=7;Myx1VP)7B{oSZmVo$2U#;yR=(HX%P>_V zEdS_HruyuVhL2TfI|P|a-eR@IinLfm8?HAn<{}E{!`=FJ|2SXm^7ZD9j6Sw)*s0;p zgFjC}tNV$kMntE`{gbZ(-(42C93BbS~lo~m@Nd>EFrN%G`gwp?ne9N%6s^OiA4M5M2>zik!ar`@_wv^zPdk#J6 zE5ObNJADGqE{q*!0!rZbi%|7LXlEh?+1g+{2B8OA8{dV=o)MA|kfYOnIFqXpda%9p zU6|~dQ3)m=o%X|-T#e9!ExzxyZz3oJT$w#OCa3)tH^x&P(cVV)(Axbd$=(Hcs zgj*wqDhVtx?pKR{ zAnH={${ND}T?`1{=ww=)qwNt(B5nvEKIoPYjGVCLziND$N0lJRr~aqBOP~&(A}CbM zk&1h=aG)rH(_l{;GHz{bb0IUmyghG}ps&qX)Qu%uX(`+annWD_)uvH2;BlHxmDFe+;3Na-aZMss5 zYh5J_t`b!W(}7goAi$6HhefnCeImu0&5<%OG&11-^LmK@N>BMyU#7U@^|tm2x?acv z3zHZvJ(yC`=9>+!J|X;1UJ|3~U5J|eju_&H_2!#alHskg9x6y&_p`-vznN!?_10^Q z6^0t~G#GkrnkJG|l?PZHZZHGUDVSkjapQrsxLYkOnpq};mQFVN)o$^Kt2V@#_?RU? z^tf$#cD!_A;HD%mI|(e6#V>(JiE&|EDD%;prXDQ{M`c=MkG>(y;<1Rc+bDz*Mv{nA z9bR@q=UHZD4#k54xd8Gwe}n`he)fTC3re`Ej-D4b8B2kvqNCv{GU)JcSeND{QxrijOUR}xY}3ao1>R{H)M6IVCpHnIw8!woIwAWXaL8t=%Xz~np{3?` z`SXVESVkr=oneAZSuPs!B9hN7&Z~$@$x-UNp;;9w_PJD9gKLv+^Tx8LE$GyUc+iFp zuAuIehGXyI6V^COa;%;;y4;>mZt*DEO&u_RVgR@@?+ffkBSGlyC@?^DsB!DinXfil z#J|t;=h^aaNQt4;@YE3yw?R>q!$mQ-s$u~&awtKhAz<)B`jqXW6k4HDd=bVH6)AD@ zF0o9PF_4tr8?1VCQB$tGa`&vVL*OFq(~ef+*d$O}Z3S$Uo1h}TZ6~gOyr9@Y5{jHu zA~uxR1>hrx9pb~s3zC}MoPelYEveq#J|xORoZJb^mGNl!L=QyR?8z~)3S8WZ*4-}Q`TgzXZJiQY@p~Q+C6&?7u9}MWo5Ms zSx7cx_e2ek2v%)LeMNUlh2fsZZOGa4 z#ER@aPqZBR6W5Am;}!(3!+PB=*el4MFuZT>`S>Chk6qng_U*FrCHqvYhI;YPjU|>Q zA3n0!1R#aB=UGOn#6BS|*mJ1ZPqX?M~?Nb>mFGs|kD%v$t(`qc||Mp(^8IvCx{=}SzJ z>2Fw6(uW{|Syb1Z3wAXsQT#m>``kL!a9j>jkCHe`oRMk4SnXpH8?*{KPj5;pKQWqy z!+~Jh)>`Bw0NG|elAYm-+)3{Y0m3QkFmoh$(?Jqx2dkR{f?2@?a>YbDvX0$O%Rv%p z2YIA0wqm6%1s3{<>0sCd9Rbb+oQ1A^2%V+CKH^0CHl2(!0cWA>M0AoUX5(+bylpJ8wj;z_Vd}+=ANO-$X|2Y62Obw0aXvjoE6uM3kO`zFy1f;9Yx!fAo;s zZ-Z8L*OzRt`hczBpf?#e2ZGDOTYP;P%qw8(j2dIeLKm}m^Eg{B{yD$C3cCiteycfb zG{9Oz{nhLqAwYiHeQ;&xZZV@JK74VsO1YtWe>IqBnUcl&t`iMZJy z->EyQ1Ucxp>!T@fYSvxd%#jwDEh+XYdf1vx|30T($#Tlj#GpX7q>z$YqY1sdTr9p z7Asumz_NTPC+1~Tfz*c>-chqP>D0X@?)R%(inkz@wp=>RG-}k$q%-Vie>`I*@Yr5% zpXRrV`vnrHU0*IxZM^~$C}EXbw~_S*FeQ5MH5)EdQs?W-r_JJVw)u8_IsXc&1asEf z(tv)P47;87bX?2c6VA)eGqkfq>o=%5dqO%?8Y=oAhHGo;wz6h(&}jB*lWdG~S_5WO zZ}&W(gHY4c+2#Q$U+s#6PLGxxVvakZ(ae0dQH@cE1R6IryI|{JQfpv=Y->gqk!-5c z7?#?A3Go2typg?zHxoOC+f z#;7?wu+4tfY}5wz(O`UF#X;8UU}fvI>jzexWUb+}KJ1Sh7!#Jy^^ij$w@Y> z4{FUppXPUbH0@bwDo(R{r{C%i(o=*6N5Xk zWp}yIHJfO$jcE+aqEgK}U9(lsn!VnrU9SzZ2HH5AZq^T*+2i$P8=;5W>$B0~cC+5D z@1bG;Tx|CUpvI z?C6(q*626kLWG{sJP+0$NJ0;rlL6GE-XdRLAtkwk3fcmzJ^(vv;lEnWR}Wr;)2!2l zTAIN0WCKv1SbDnr^jBERwQh4T#4HMFN6Y;4%go?c<&RBw&yU4~?nd`1Ag`0&3R za2PU^)_4e2BQ|H(ZyN9<*J?TaVC>v0wxyX>jK@XM$m-pCqu0g8f(n@H*O&Cq>o?7b z{5|I1{FPT7UaOcqDp1mJTF1qV{&X}MOa(nmG1I{$aL+aFQHUE+Ak?glv6F#s{Vt4X zCXW{sM#~tPnCmNviK%4>Zj%dn)99k?Wy5}b+-_rCp#sCKHT}sy1fLwd++{}jmB1pL z9Aw>bd)lsH3que$^n)ufsXP*lq=*9ypW^Ny8xDrOVYfdqykK(OK^s%U7(DTt2aP$; zpe%UYK%@tlh>gy;-^X^FaHKh-emAIZAL$2+3A2Sc!s8@Hhne$@V7Mh77Qtm%pNe#sU=+bgFzI4Fj5hXx zpt*^^^}o2;XZ@#WmL;3zi^Zj&#ZES8)rXzdKnUCQ1!0mu&*`P!jYVrl8Oa%Xv$^R!6GwZbO@zlG6!Jst~dW*TsZ81+-sG^j(E%RSI6x?}MY3aHS7huIqrk!37 zD-v~rDP}5IdM8TXv~#oVB3W*`RHqz$)Edm_Y+ zVLhSM%4QsLnrSrr<$fFDxjiO?;aHt9v2sLhLNKK#^;)Apt}`#vQ`%9f(e;fvp(nee z#h4;)oryL)jEKi58^fK{X-|ZZ`qTddaf3&Kd&uI?>^D8+Z`>NgnSrpzk~vdpzD}}c zXNcWTSEyDqz=q-6G#6Mc@ZY9wW&yX#-&k0LU@+fIG1F|Vk+$448(F{Cf@ceROX33$ z7vp=S@bJ{-Vd3Uki7;~h7ZYm=OfMXvZnm;^t2gSk;B%pZl0oNgLtG-(L~Qyq4NRs| zNespG8RjuB_#0bIT5CB>>}35u#*-Y)L?`39e$yV(A1Cw!e~kIJqEY_)lU{VIg5_muxM6O846 zNfblN9pP!oRs4@)t}>9BpUkaNXi zwDb)DXG!;o(6hGH%W(A5?zdZ09u{ULfTO!)<26zmv{a!q%Z~Vp$Wv`71M&`eJ(9$?EmVVA2?i-ugCF z$rg$SjBO(xYcrDEk{Lf^76|F*ap#X*MSL{1S;Baj_?x9Th3whnPlo+=x8D&G_wPnN zT)NXqZ_>v(jU26>7S4lZ?u1bk48OMsSjLa6B)4dB>t({|rCs4}@w##`lfQ+mnL+Ah zJ*cYd@Yr=^I zvxbI(WtA13r5xjO9}{cWRT^FV3v@)PaWTWWV!Tn^9R$&|I_&{sCb%~&T{x*AnLDGO zQ~JR@H{0fduWbV4<=>cBtP=ztrihD;;mH^o#^@5}q`>&6#>g;x`g3Lz z-fJVC0Y=!rALWRusZSeIv9qFVoelKf|Yh#4oh^-+rnkPCB z2LBKiKhw3f~blK;u>0(*6Txr5phihNDDY{ThvYm zJ@^zFEv{)~fN_}3AQuo-x+V@4aoUNHw3;wejNqEy@;9?2@b-i0;UcuVNZmU)3M=!gjVGOfh z9*bmlVIuL&EaX$ zUR_5-Uxi6NOpR{#SBI|C_PW!zG@vRSV*dLH-co{C_sIFb}8QDS5jc{1dc zQ1mK-9qzKsDlk0qOG2r#s>SmsS^yDwvPwf?vI^spit5)R>$V4FwlQ zM!@F9^o%Z!Fjtp_?A1ka z20O(Liz6jAlVimWn+-rzZE$hkKoIbD@vws7`?|DDpL&Q>M}CeZ#^kkV7!EQ>5t)NA zu2x2IAjJp9x_uTjUPvC}YtC;s^LyN8xs%vJKO#x0?A!!M`T1&D_mtKmr&A(n9X?*N+#I9zpdoPB6C4e1{8Gnsj;p@qG;$ zV35eq;Sch=)c?E?nW}TIo?=V2iX3(BCETjxRqhXYgNnY)Z!^JuYURr=i-UgIIe-qW zB8eBGcvT;eh@H1cd1p2If(sn>Xu!Znwxg^t@e;;cBvU9rkQ@drG8)BVb$c)cv{`G? zU2>2RuU|$0VaPv96u2M}$5PA(n~EfgLkJPG>vgIuANNG2nY(p{h^zR~P&+~-2({2-AU zUa1zNJ^J+)E!f37dTBRHbBatYT)Od~gMDr^$^(IgA z74B^7xi`ke$EPLM^D4lT&C}=EiVXcMofuM#;bd#gglhR`)bK~ECRwbffoONSoqgfH zw8=&xIl>qS`$8_#qQaS8;-ncPR8c{7rArFy|2|v&O`NPDrTyo}`EGHGqyn?WZksh^ zy)J3IG?*mvtOTr84I8f5l254&8{M<10486NDZ?h8{wf2byF?WlrOUq+!06_B1+Z~7 zut_yAyromA)x2USunySx<_}mxkRCf?qN%y^es_nTtp}Hr(Jp7GV)oFDFcHr%e_eE7ux{1Qe_(!^+3mb!K;WnfPiP z^>(Ibof}&fqav2xNTPdT?#=H0bHCl;^7HbJ#L_o3y;(pD7odjgTa5EuTck7*cw;W| ziV6BS!&fBpISvX)U<5o28L}8lYS61iGV{5Ul2CR*JhVWI79*V@Q-$@&9LyM=53@uOichU^Tvevi(}1FvhDI+Xk~tJpHLUDqE-WQWjlXmw zk{?0gkPpc;C6c{syyB4y$_rw_xJ_^mKK1DBNXBKq>f-WUJpi;`-y91g8Nc4ilkPGb z#*iC&HYnL!|BV%qBVYF)xph9%hVAeX;a84I`4-`rBZKNChEhaEbf0r1TbOTlIVaEw zn3IqFE?X(NW~*=6es}*)Jp|;AG$q!>1|2cZG%cU-5Ll=OJgXN_NT&h7*Qoni3wiLc zE@BTVSI2(=TJaXgQ4tI8WgO+r=Ftl76>`sdI2z#UrtvsW&*R+ zyRIN$%9BF?gHeLS(n}=4JX=NcK!NjJ7Hgd>m=_=^TaX%4Ij7a6cJlS+^4FWQcPDQ% zP6Az|*Q748Rlqr(JB%wk?ysCKQ8KBc;t3tW^%7Wrg4t}v1Ap*3r&{qPya3u~9jcx? z4KcG5T(kKi#sdt4OF6fj7Uw z4h_b+rvs1}Oqos+ju#sb3XvzsP~t;U>ZSnUYguWxLPstIV{{0Qd8--@Q^F7K?9kYK zs>kTqG%2mP%?WetRiK!79^7>_BW#Z2oGS`zf?r}*_QWv}TT1-+Xr-BQOvHK+k8)@$ zm|Hwz2MYkKg{p)0a@=L(_FBx|(A|EXZIsJWpK3x?k+$MvTYZWig~W=A&2Ab{Z7|7b z&p*=tmEovl$3|{1a=LbnI?@qz2X|fKhmAFF=4if8;{o#}?Q83S;)>GCC&-oB*LyO&hDdP${P_L5q|7u6b-)IwB}&h{wXPs&zB5%NC9w&m_Q zF!|)Fdh#&MVhG>Q=YQkCQ#!p%<|5ntI*Y!ThNeoG3mPHHc&N#K_!@S^`yNlv(?L6l*%GIAF>ZO@(OYus5S;Uzoj&?h z#jmjGp+T_(QwO5R4qwX5p{GFTgd};56?qN}=;!(E-_pI6@7B4xzuq!MuK5bWb=%MC~g+UG{K;Y&+%g9jvAbkr4F%5z3 z>UH;KWOdL{7JT@$jibcR$Vshs@X@zu>Cu?D@193tz4}5(LU3h9?SQ0w%&%016&{gYc(L8ia^!$T z23dFnT;AbjvN0Kf2yi27kYDl$u|yqnyxVf7-)RP1()D5-eZtG1LYi})+gZ3h28W$K z2^!4o4LYJ8eo`%!R^WM#M_*+#9H432sh*)sLoP-)fHW4ZvVZmFT*hpW1gn=Uq@<_= zRt3RJ4JYf4?QNBivwU*MVB*N@cF)@0UqRe86hM^oT{^iEQ{=k#@q&sT>EUm&kji>F zk{=h>1+$1!ev#La5m)^9JjXmaY=lNE!gq6ZI0Bt$3gy6%XP9J{1|DyeqO@I9cf90ZJlYQXx9ezL|cn|!;`e~Q*0}AAUbs0o~vv{*X z4#R63p@$@n(uNFgcWEu|2OVOeC-^1VT7_Zhc2ZqQHFJJ;{Bh~|KUeJS8sXG#xb;I1%hTs1G#?SPvv zY6lWP?a29mKie;NDkLCw0x$0S-pvJ|(Z!xt5wEZc+L!a)JQoX-j!ALnHK1YO@uJMe zz3{!L11xFf=(@t|eK&vFeP-1k?lTcH&!;Z*>ceL7M6tUJ8Gmz5t)Dh9(^3Y$+pK8^ zX%@>DE`fqgu%Upiz<5D#1W1|nLJOXS)10@)2C3+@HF9idNj(cb>w}_6ElHe1m|`<>Ju~_`FL(+3}ufC_CIU4P{4rrlIU$&oq=B>zRhKLp{?_ zc9diq%8rptL)mejX(&66G7V)%d8VP9AkPA@YVne z7s77s7-h7s7-B7s7WGL%Fogn^Ma65K^SOe1MQ+_9RPQldn-`NAFws?>RJg1 z7CQXzgifO3plZKG=;Q^$ut^JusOKJ7D7Uw`-;nNbo8e{i42w`aFgMB_2Y9!pM?7f5 z?Zh8**CoC*2z8x3NeM{5Q*^W@&&t37AFl~)38T{T6EjtNJh_BhUi{3li9QxiEYC)2u(J;CsED^sIuKrfEs1RY z__Py7pgzttCn4gkWdmY$3mkGv*+dj3mwYGn}f$l47>##1!aVtZrJ0Rf?Bwf^k1BTvN zE$zYU8rzeuSLZpRU*6O5G|JX^kmS&;C2s`P^+wgJtclZL9Mf)wH$Z0(n}@8J5NB+> znjqs<9l1-fAa8XV;?w~lKIi*_y7Dt71h#V9=iFh97 zd;}uS!1&_4cg(8RKPsyfPjSDXKq78Hd7tbnS}@_&Bv!>3X&6f+J5(3(99K;ZazfeSDc=t26A@Z94$(R_>dkpuOoFbKy#8O{5xR8Lrw z^2M66_24p%MkSAvC9`nMkgFGYWi{+W-(m+zjjImJI{N4&RZDpBb*=r5UK&ot$-sV* zaV{pLh5D*35?55sp3+%WS;re05V0_xl$)w*8qj$+1%g;w*&iBa0;vt?Q_Vb8?dg`+ z?PMLdirKG+vVxUW@e7vQ;w~4Do(AKLGcOU!KrGT|0s?}?J#iw%Y$F+Ko+=r8=8c?| zL?5*hGz}myE8_Ve&&YAb+AQdr0Hn`=-8$PXzRVBCBK2#wOhsrN>#uoug;{2?FhWybbRV7FBvlaPj3}C%b@*P5qpABY-1eSXN zG5-p?A@C7-k<6dxUzye_po@pkbh$hkO@Rm_;mrALAfurQ6nEFb|9FK^z^S+f0;aeH z2l!ngpd%om%1wB>m>4q33lEof^7VM+vwKQ7<8R6*dfsgF=c)t2eFT_+`+1ee<8lX~$x!Mb}t%GK52ykaD6p4zc3LkhqE_ z5&|Nt(giE$_zr3b$D6F;B8P^e6#E4dPrB8;U)^CV9$gLm^0=<`p~w|(EpNQ80A~Jm zhW@77u}YJzSTKn;7W3Q7%d2D~c5$*58jA<$n-rx^c{MBvYL|=M4*L&r)Dba4$ld|t zkQr~kqp1x=TdMgT%-(6g^ZNJsj!hUI2Afl2B*SD%HUK8Ng139BPoMZK^bLmI>e(Curfv! zeNu{1g?o5_DFK!uz{N=cmKQSMi%2WA0H!*&*qm6Bm}|`*7%JgCTdDQA9@`Z0I#|tV zk}T)<(Tj@SLZmk4@dHm~^hzkuSAM;VA8^DWhbt|fPB16z6?0fp z$*1Vw9}*_hF`6ygPSV*47cUf&|`8+3r7T0a!TqjRhh&CUe zsZMIlE0Iz)f;>5-apVci`TAMJ1Xt!rlyr@qoQga#D&vC-%JWwmnG(tS59&Okl}hl% zt~zmwV`|5l9%4@^FY4+J!NX;opnh8-D4RV)@H##Et{sZUL0W_G1Wz#Vx!ML20bmla zeE%q6D{UVoZ0YV%!zG)C>qn$fdqk4ve2xtM$R|y?>^KgAukl$QPqC3&tO@KkI~V7c zT+bN{SK$>6m=Gt&d3}x)jnE&Dv#+_1femakWxY@q$6`ojzm!8N3#J@W*)Zjh%8Dt6 zRCY``q_SknA(bst4ykO9a!5NowwUwEOex1{hsqY?)YeovQYZgz3D)YQsu&^;!*;Ox zR+XYqU9F8>7x0-`1nbjS1nV=@f-NI6+y|wI3?Wk_hL9-&gI%HGqB#7rF0ROVt*j{; ziU39pRs={~p_c~Qf$?6_Eon9BU`tv@1;>r-=X`|g9rroO;Sfz%`?y%?i`u2qR08V^ zRSB#!ST1Z-nk=v_=(T;Fnq@9Qc@qANusT0~``M(Uz_6b?=^ZjNKxICmm6QP0Z z9DL?>g03JU!=c$f5qd#WDr`#z6|E@W zffsXf*phPAgB=?~bMRUsFo{cvseY~DCypEwdH#;|{rwi_k9YH%eN4nTL~M`?*0^^@g&hGWpm2pjOa(aw6^O4)qVt7!7--iG;1JtO>x#ZBP5c0Y z`-$|riY0p)PReVLz7Jj*H=}TAdo8&!l!aLsd{&T-Vy5`og`??+Z|v4$l_HO4#3UC< z1_eKpNZ!-aM!GcL%(uArEf+vgf`ion`-OPNOd@7MNztd*7VT0Oing>aNrdzfSXH66 z^k)^VV}+yYG83~#35sN7Ft@2Xc*zHy;u9WHScvGDugG_d%kVsJ+WIvzLUU9vx+AmPp8OQC3+2S4rR2-A2ztH#)~a~j|iC%$Y!iEPC?Ys zba8E36x?>7HgvZeK}i+bHnr!-=4|whw)sj_j;BJEx?wv)P*sjMGLI{u{l^|l-R|45 zAB@GR8s-x(d;R z$+x8Ynukw%%sL#R@I0bJ@smg+Y%5Mm{ftWCf{k?w z>Hi=$h^RbLmjy-yfxcEE{b{tN(;qVTl^J+EwOk4Hi!?J{1H-C!@ND$^zNh4&#~Pvi ziuA;hhWnW!vHvB5QU&vi#^jm9P5%{gR0Z?FB&RM6y=u=YY!W;qiTE0`BM+P*so+1> zyRvrtjGk+Xh`Z{*VU(Ryx7v#*u!y-==?7A1AEK29uP%DQ-ps!&)_ZtbRDN(~iTO(y zHttqd)v4%F0)3*5GI;VT!F-i3eUKhE9z*h!C;_N+UyEEZ!SOHqW)5j0>Cq79joRN+ zgp;^0&olY*Vv4N&%@(<#;mCk?(JuNb0rh-f5AMjP6VI}|-{n)ury`UkT`NQCd68a3 zCrP1AXdLIEggRKi9sV3Qdd}`5;$FOZIYklyE*VCz_(7V<3BZ#bHxUsadHc6qHHG;C zSJq~Rx9J^!Avq>uNBqXT)oC?Fqyz7ga3}Ka;D~|E>~8TjdLUty1aVSy{AHDU(NSD_ zM>o8beB&l(Nls|K0j$7b2bGAKFzYu#BV_}YXeM1vH6-#r#BZd6So45er5Gp-u()Fi z;lSB3)L|4ynk+XN10CvUH0lXQVVfCGP9S24^5H-q(L9pmZfRFUi%Y)!dlBZ}P(Ex+ zUqeW7C6MoG!8)E(&I;i@&p)r1VJ;bT8!eLdSJ0(IN@zOM@|G@(4B*vwcg%_=rp9Jo zp)SCj;(0i=;wi@_;(9AuK=F>Fb(Po7j4Q};4_B354dbA_D3dxTt>2@3qFFx^*pQvf~zZ)NWw0K1MD5fp(`oKn#6Tld^6|k0@ zw+cx*=#uWJKr^0%rg7D}luj;U@N@N>{o@m*06)~IW+QH!s$If8s7B5jwY^>7Y;bYE zsFH-7HURW$jG*)|o59`yaJOH93mb?uit>&NBc>WbRk3N3mbh2NN&;(UOkD`|s1{PO z+qHGASg5zKOAf>X0n?VYb4uo5#C?|sT;pLaRzhD1ZHHkhrB11BMY>w7l##?fJBw{X z0E%j{%4I8q>&6g+s&YKy1S~{k9YL}w2V@-OQ3=x7D9OAFaZ)19BzhKs)U>NNUGPW= zqT)E=qB;lJN>)?OZk4P8{}4CvB~9`)5&~^3Rc+SkM|{w8186UlG>C@RSS58Fkv`^i z*Fmz}AkETPB)l75sEdt@pps>fDA7r5Tt8mO;~Z~(icTsnP(Cnmav5cV)Jl|7q7aU4 zQtDI*oEqmcIE%F<7>zs_!1O?g?W5NBrQ2cfUp|q9&V`lBb*Zk7#2hbQ5)+lnn-?o0 zs?*KyLzm;cueZh91d4JB2#MMiZ$Gb}K?Fxs_14=P_@a0NjT{WQXPpx0)0$k9<)KId zmg8c}u)coJB%^zevtP22MW-qJz#RL_&AAYK?C7~+9Iv=bfqW!R zEUlc!RE%si0+o_yar7@8^yN>KMomB`)|sLw%w-s_uj%Dy)|!`yGg9$No^$hf1X%jH zpf!<#DUnKv!|a5qBXc#~t{=$=2zV_*NE}eSi!pnO#UoOD9WpSI3t3!VqLX0w;gCyV zvD*izO^zyczte%|6hgjFnFgPpj!^{7$!8qFtZgs&U*QD95Z2v%xjcN| z1}<2KHl!`$c*-Yw3==}WMMQHRms#bv4da@nwL__R6$&AuaA$k`E=2P2g03Zy+4mKX zu8E=Becr5}#l1bLIM_oL36J}v$S^hX_4=`_7PE0dYr5P-ml{=$^S|tDQTV}jUIS$# z6m241Ni{;ZJmZ&Zc8k@w73aPZNo;%<7T=+d0tGHB0lMZ?s=_OnNLr2SrEGuW=|*wg z1Op5D?5ii{LJ2*E&55}IP3J(oQE-9fov8P#oW>ErdlEN+A+xCk`M3)_Vp&KlgB3ZF8{S&bNql#40Ta zmW!IJD`7f|Oq*g~c>M;mV*ao}92x?OV4T5J=)F0?d)d3qsoaLpHH$QKKBl+R4K&Z= zVsHn$1y923TVbFV6NH!@-^l9X=g&I5xPK7x!2nUVM6Hv{l&p7*sSIHbq6m z3~{Ea3QlOtnA^O_(9+rY1>!YG-`h$^D3=I+xvA3jw-~ZKZ~Sq6o&SCUgYpK6P56jU zXrt`i_4SGO4NgSL8h=8j-Rapcer>4Fombg>DyVDJ2+v?{+51*do08A(k=M@+%3vh% zB8`g{=d%;18(64!3%nW3IVG&VM80b@Av3n$uvvW!kc6LY93CZNR1F)X-;_orUKGJq zI}68^yQ>S#_?YjJzM8^#+oJpet;Jc6@;`cUV$64k7V$YwiYQA`@<*?b5U7o1b|U`+ z!koYT@8|b(^@)Eg`1AYw==Yld{u$}1v4{U9r+-jfiTD@%xxf6c|LOkc^!()Q+jnR0 z-krGL)t@Ljf5%1t`#=8MFTeb9_kaHTFX#W`mtX$d&;S19z)ueRz`l~_Zu@k%q zM!X6vKr6KGItyH9MM)>zs;8RwzqK0!3K#JL#8@>Xzyd@2v*VG*fEnaY) ztFu!a)add;;YFrSWch61sms}7g{%EfoAuXk;0vV$L7p`7;Mll~gi_!NfbcQJYCHnHrM<^J@GS zHv1q0Y-S7qCmyaY2SlSHxs*o7XXfJlLd9>m-AVe*TH+z6= z$PpKP)M#w7sI`;cp;-7W#ekNuHNb_9X)xSGc)b{ELEvckmy4i9)n@%I3niYrt6PiD zMTw+GsW<5LRPv5WHX*G~;ZW7;zloSv47*j|u&)XNK#Ms(qV#_B9-3^OCRk6tkA?^{N;%TqUgC=8aq}; ziwe(9asP~>zn?7;9#D?|nAC&m@*NTJWO^rHGuE-rv@RZkLR5z2sb8-btA~LRIS#2F zg3;*i^7CwSw>6=9)=5|lgS6c&DDmd*^_x4k6a37YQV*z;e*Mt2%l%kOshVmys1@r%qRj^KlnT@`lj{BA*UC?4h@bcQee zxgiOsSyg1bY~o!nlE|{fpRLu1C616wZHOyW>BaP~8sYKEX{r3P7>Tk7?8Vr@0)QkM z9Xo$`V58UG%P>_VEE$^N_fzdaifB6onM=a@nO4rcrHT=)V0ify}<=_9GjDE zGYeDC@{JjHUh?iCqSIi&q_Jw(LM7tVHSwd-6+@^=;x|n&w-HYv5&*)>o);em>@@{D=A|_FjJI0LbJ_Q^n6Ds1_d(U5+C#hYjlY2 zgiK|{+)!ARDlVodHu^6oCWg(W4H!(lEM;u3;l*zgTe znAId_6=pV1Sy5$_s0^`z;Rg4S+9OwJPTNoix2J0!*kC2=i?V<^_g-`PocCdkRWrWA z#`?LlILPFPEs7>7gm51%=e{%Tw2H%82_OQ_mv_X~{TrJI1Yw%2;@5 zUT;-YY_5{(FQDdVOeR8N$duaWyIHs<_W?&blxi7~{T%;I5zF2z7IP@!A>wz=EbU0U zFpe6x+(QK(qgeCGPmw0^^v9BTst>Z#B%b|P63;4-c>ZHar1VpAcG5+~s4QOmSQ0NP zka+oHNxZB;;@clf;@b)&zWcExzNb^p;-z28F}^|Jb+K=HUl^;^9R9)Y7oqBh&`x^^c|}AUjK?7K zU~A*MFxfLg5(08`+7D-PH9`-zm%a;=Ju@o76 zv>(poYJ?uV^Z71J_BKQbCLf*l!JR60MN$vnL4OxU`y1;7qmNJh;f$_G>fv61@51Pc&Vl1oe>kIUQYqn=4wD6= zdh+(fmI};{J)Sk)b8I1^gC6LR2M_veLI+4N&I1Bx-H>Ah(N>Swb=$O#LR5ku*Cof4 zhfp>zlF8v?Oh^Jtj7X@1?D2-b)))rpG(#;Qe4~?T;i*R+om|`jKzxvsWKG2d@ns%W zf*_yzpYkq&I(Ukp@Z+p393sNXM{-g*j+#;|IH64XLX(Fh|LB0ZBn4PdEL~iL=C9di zrLH{?D5d1NMuBlY$=Gf?;X?sPSYMG-gusST7+w}S#*=}X;;=c+9U`cGB|41j@sO!Q zb@2I>WJQ^kj5G4ZLVxp>7fBH^Wpk-vASPF6vbT^utLDuqL2WTFHvcH zA_XZf`8D*lC$M#nsjood7v+~Ls`Pg~Qu%wN+r*=RgjgqusuXa>a<6k?gUTXI zNk*HlQ0OP+k|D8<;;Neeii1ciVLFhif;>J%bfj3bIZ`HuuH=OutPzy`{1bG&;0z0s z7%e@RQqnlv4q_eQao0si68NY5ju_&H_2!#alFq#`uF!*ek+|XsXtb(R+)aa_=cZ{Q zNmYry>?PH+eZ@_;B}JlISTxnrOpoNf@v$aAG>R?Hj+aghky+~cB!Q)}_$BbXXhL|j zC>)h(5n@=Jgo$QD96|{rNyMocDUMs$nTlQGjE7Xb`;Oc{kYK!Bj3l~ht#?D-#R-{J zMIWBg%j5f7l~5JwpJLhjw4vaX>U)LKdPIB;<|V&GA3-lm$XqLI)5j`B(`gyK#4Msu zZ2d+19K$1Yi-Sth5^%_7s>^vX)P$up;~(i<0xVAoCpfQSaV1Bo?}lbos7Q9HvIf^C z-R6yDDqGO05%Hi69b7@(DGkTo#V4$B7OPl2Yjn9ipWNb6w3|9$0R0rCcB2R_#Ku8B zv1M?Y-ZMZX!!Tblq{*Y_tBux9+Zw~_2#7oEqR38NCrMs2kfuu+PF ziuksj>N-!wLlN_RQhemFL!faUR<{-PfryhkVa~azlJIF?LC7tS&?G+50}(bWQ~MnU zs~fGHT}X4JFaR^c;WXhgrdtxN;xa={&_cl6XZ<1Omb_9}n9EScTRZbz&saw2r$;36 z(VE^Die{q^8_-JVc7VZ1bJHEGuoqIl_#Kf=IxALc@hhn7s@52w-@cuIIofTZdXay@ zXfyY+0PbPyw4)x&B6VZ!$1*yD78}$VJwr(V64TchAniobmkJOPEPJHOjM5^75EC1b zfmO{~=K(|^2pX5ujw@|kP27+w77wI%zWqDDo9$*La+DM{^!!AVWOvWj)kU>mRasf> zV)`VTvByxcO%4rh$YRBbySy}sqTpH%iy5w3S64DBLK{&^5>VXv(}P@x+(WNQsi#m^ ziuwhC?HK+lA5GF=DDqb+N*(XgNR2F-aNS9!#Bu^fy+o(lH>JG;qOGX2rLqgC0*)jf zjGv+aOC^I&OWtX;Hws5(N<4oJGGmLGbz0`H5gqZpG^T#&b+OB0Ihakk?;a6tMO+xB zinrT8a8whuD|6ASuSTKJG&dues9nAFAb+&sOSMg{U@*a|EvaP%n8I++<2K~%d16KO zo+nxk{pqA>m%=EGP-XrX>=h)jQFG5~QCm!&S!7rDmwmgee91l)G@GS{-6SwZ5{r!w zA6aYykV4z@ETdFnpO6wv>?_a08Tp<8a9c~H$D&kRf6N~67!R^g)tda8%k??X;EEqP zX5sBRk0GJr;?m+KE?&DTVOI=;)UxD{gi%@iKDy-0ELiVC8r?mBwsi%dVE#oRjMzvQ z!-(x?F^ukJVQbRLyAX1%EiI33YY={oqEqjjTyrs@QL4r&(bF?k=FCl9@m;b-i!2u? za7Pg`AKIrfR$h*XJ5{u6q^8wayfbXJdEm6n2;&x~*NdwvjB$a=`oqu#jM3%gh@zuy z_4G>cqEb(5uh%8wrIBkN)5|8k%0O5%<7qEB^%nYRinMpEu3vr=HY~=o&+AWpapxpR zgcZS0ZeG_tE25eOgco>z%j@g)Qn{edGJe)Pi7v?nY2sE!Sj|Q{7~RY1OH7gJZ&+H= zhah55sIEH~>}ph^__(h#AGO2|5Ct2{;Q~`w%)yfqle@_H8;DX9CVb*NNyPQOw5Q$mvF6(PAKK zDp5YJf`owzkW8u|VW0ve(<(?9r~t|DYC9FC;_2Hi0%2GQv-|gGI)%efRE#T7 zgg6sj(89y~*XtKp&saYaMt+9uI{#HUj%3)|+j@q5i}|d=lhk+50ir(pjt%ua-2a%8 zm{UWQMw}O_C=4J_B_x93p4%>UcHCisz2AWoICPDV*(y`tf0 zG9L8WsB|}Xi+U488})mQY>8Kn-J;>7)~`1QO+37~_iBw=qguby>}NC)-CEOrbKDsY z`q_5Ay!VQR9pvFedifMzAdDjLj_^4F}Cp11EB@dJwwg6#m+(joZCu zzntXW%?Ze!t&qqY`2*eJ{$Mm2b{a$)=YMy3`EF9{jGBX5 zJ9}E=g>R&Ca%x}ITS&e-YL2t}H9Td{e#fu6onEV6>u1`4bE^-gqfV_o?y-PwpOA~) zEyq-uj3H9U)Om}HcHW3wfoH?|xCOPlzln_4)dVs=Y4s+U8uHm)2YtPk)xo>=2>)D{$XtxHP36hgO&UUw-=a7h- z4cO|ADnSnV?fPg6oSJo4H~ez5+j>2l4C~WzztJ}Ga|Ic?3iL+3kqt-fL2J;&JlE22 zwO!l<^8uivVW-&|BaOHj+Bj%4gMfD@lio19!<*mp)g2zDy-twO%(|VyxY-}Jz=6f% zlgMXBT%hO+w`c8aGU-l6^+{Vl%y@l%eLi2`-z*>naEAKu({6V#8etZzha3qWvn~$b z$=ahqyW0TaHavxO#w&EQK_4SBz;g0o1yNi-tROs~otJ~l9VV|!Q8yz|tJeozd%U;* z*h4?barY~X$HPW@)SZCsYf9@+Q|bx{KRJHSpWS#|d(!An$I#tzitygD+dwaCw5P** zZPLvaD`eouvV18g=4DiY)Q1?}QL{Da)V(I|@kBK18>qD9(rKnqqh=~`AIaV>jKI4?iX(9RC6-=OB~>1ny3p`s6BxVENlD{D3fjb^Vl$;K$B zHDE^dcF*%U2sJ&OZ65Y`VV#UeGH)8KY|!b^l0(dKCp4Ow&o-(tN@a$|P0cRYI+)ZN zSRmV)kwqk%sx*e3W^V)recX_aG3+2>G50SwbA(Od56+;bsqJO0?zA>;j;GmFWmYca zlilW|)9E%w&EbJ<_OoWAHmHvV;{z)WvQ`HxTd!R|u;L_Z4X5>Cf84;B(2TiUKg~HW zZ@jOjlap*%AJm$IKF#m=Xxg*VRGen@PQTS3q^AfC%Ci`(C%ayE+QM@%Q__Ziuh!2X zRvivOb(w#&vb$X9noYFW#x#ayQK{yguGy+*&0cTRuGfZH18tm5H)}j8{&;=aUf^|r z>$B0~7Eivf@1bG;Tx|CUpvI?C6(q*626kLWG{sJP+0$NJ0;rlL6GE-XdRL?KcYt6|@CbeE@dU!hf}# zuO7Sxr&*^9wKRe0$p)Yd#qo6c>94SsYu)Bx$UCT8YG_YC*x0a}J-x!lsookv`O{}M z$!GL0UVQjp4mb>%NozcWsu7#B>o*N}l54e`elT|K72DFxD#qiYXk_(nz0vDpV?hPX z_3KOe=k=TBME)N0Z~n@w4qKv39u+8QIIXv`ZhtzO45oq}rkLqq61e9Y_b9}TC=hDa z#@NZgw|*B!G?T{*3ZrF=Ow9F_#KhDx1h>hBylHe%_OfBWK5niqr=SkMljqG4~yWktWQO{OE8My zB$#xu9!4AcK+xR8-}+zN?6dw;G|Q6B^2Op(&|)VWwCcl7YaoQ}`hqaYpJ()MuFD-1 z(~duKXIKP`OmcJVbzw_S;NUt8%|8gC>1_wLFZLy{A2s=cQYM1`$>jf&ikWrbdXN6V z?>_%l0yGg{ZCPcH2-j!o8C@u=jUM#YU@&Nngx+HAa$C$(7OE&EZp-`^4+VFgRa&~P z!v$C|lWC{d!-_-LB>7jela(wni+^e zHfh7{)1C;iV^~jUwXzw9oMsvgf4Sd=cy5mgVK`Q2OspJHn-EOtNxjzSkL%2f^pti~ zYIJ>LPUy+*XfdXUTW6vT4s`5U*!aAqK^ zv1HCvny-_r*%@N@(-o@K46tE1H_ZiB3;effn_0lE@;4S1AsEazQ_M75YoskV%|_Pm zwcy#p-jevh!^QYsDLg!Nd04o4Rw9g?|HZ_b0@DjesGF^<-Rg~cE%;ohpk&aw+YpzC zH4&TsOaqgtR1!lmeTI3=3;xDdlh#@e6FXVIkMSf&GttR-uHUqW^v4PPz#n7&t!NZ~ zIU;@iPC!cf#a|M>(Gg|xH-B|nk>*nMR^&=5+q5PyVj!>~>kI}HVG|ug7?@N>#Edl( z&r%r^3sZw-gGGfA5FfPG1Fh5Mpx&5Fg#B;~TDhq`HN&&sm~Kq^D`>?c(Zg1w0f#tY zXG{jWS%jGYt*ax#Y}N)L0tGUX0F6x%rpM?wp?4f+k02P+;iLg?C3V0IQEX5}2N;H# z$vx#i%>-jPU=qd9az}Vtauxrhn5ztA<|lJ2q1;+Ecw(omQ3sw_qF784Mi5sM33kP1 zUjj77S?)En*Q_XpZ0@DdEt0YZ>kYxca2wQu$<^%j;hz*MJtJm*@Zd2z{&9c;XB^Qb z_=5Om)+IC2JS}}gz**9LBJ`|n^)ej&wEOMWl!t{`$@r{sbWrign?7Yp;czIVqabooK_G*lmv6*l?TK@b9Gam9TZFn^@MwZ~n>*rM{TncCvbX zGMF^RqPM;cRkDTR0b|>U$J&e}w`9i8m<2-mdEEIUR}mjgZI&<|CjMqAP9b|X`IBM4 z-R*aT#QnRG50~zA(wp>gP9sOFr-k!inLA+=1;g(x0+#V3E6FWd+UasiI14+PxSyyRFeg zI??{!L~W3@=`^m{!PB5ZG1TSn2L1l8KG8?nu+?ccaFEaB|DYA$n?Xt>V|GLW)YuVP zvbyl__L^{F!K|U7U|D5FXDP?H+{eV)b(KaJ{{kJ6YFx~4t{87rcLzZV|X$~hB3N?IVmvy zsWCFlp8lNKg!kHrXMhp*??*YJYUiTEvi&iYXHwBO5`1IVMJV$ z0n!2v+!nRdK@UC!1Xxg;Mg|y%*$i?4LFk$|RK#f~Legr&OfiCMdduI;lEB*!ri<4O z{+C*qV+O5Pld*XlVg6?N%S=)zV}mKE<==j%8=D+wo$Epsv5Xt=R;d@ajJvH? zp&z~MmWDCRet9gC*@cP3GrIs`RI^(ik8KWNoCxPsu^s1}ghA|+bGO*ymk7J}W1c-s z92c!ZHu$;^-{kV5@SSlGcy%2SeHGpSHk5R;zdCfCw%47$r2$px5cA(p@TRgqtl2x7 z@>B$4#gU{)i4r5L$de(jgrZjw>~NQ5R)OJ>UlK}{RV|)B(E^CblT{iDlT{d(BoDw5 zqiC5eLva8{fNYRx#JmJJg%UogWo)iHLnOv7ELP*>mwSWd7yGi|mwN-}7l)%4Ld3L! z4;2|lA0!-1A0j@YF(_wP(*X9^7C<=z9Kc0KSwNk6)6e_r4(VBRB+udlcorSE$X$Nf zWPGB@!sOtbrN-o}XehWaG6L53knwh5gx_azRV+Gi9%Zxs>av-Ffko~s3*kXVY?e4b?Ud5X>FMQrB62%D|%A)D>O2%FD~*nHu!S>FBmU4K`M)&(A`^>?4y zx*$|HR#8mP=;8=-by>(>T@+`qQ|z!fQerbXR_w6Z07TUW7v~KG0dE%%D;U17OUv}B zhd6cQ=SX5qUWE>RgDId*LX+;2gM@hfG6D!g{!ya91&KJ8Vm{bZBvBkfh?rfkQ)T(M zCo;|Cod~)lH3fRiVC>E|ceHY2yz<_9dxN|GEL~?GFX-a+~`ZuVdjq3SD&x?Cmm z7>Puwq{eH^Cz@Z8sW*9=uW)Bu&%N<_Gk;oQJ+A^h**txot;o>N(upC(7*4jwt}K{(vQ9zKNULLfqf)?r>Q%GSAzO zyZhzrVXM~I_q%@r%zg~$gop7x-efDA(6!nJ`G`*b$KsB5C(cV)T5rq3*V!bWU_$W1 z6T_C&0tJfJN9ZU6G7Q+T`-B^2=-6v$uNR;?oN$c|H~5c5SOnF}36tAt;oRWm-lWka zk${3VWmx$*yUy(HE)!pkqh2Qsmnp0q-#;p1>5U}17v|pV?mze29WFmF??^0tL(`iD zv~U4xxW2_W&$UHL6M;A8BCnXBk28ElGN0q1fCNUs!;m41v7`pQS|l@{J1Gfe7euC2 zTC^DH44EpdN9JJ0@O+SEE*N%x`V}whkuIS{n4KYn+|fnxsWpzP%5-`fQ1sH!m^_S8 zHLUDqE-WQWjlXmwk{?0gkPpc;C6c{syyB4y$_rw_xJ_^mKK1DBNXBKq>f-V}mqqLK z&9N}jZ17H=beF|U5zhuCdkc9--x!oP{US%c?mu$te5MWC;UmJY9F_7d!ZAk%)k_Se zh>Yky=Sa3N-|TWupc61BANyUlQgY2!-?IJg{+)UV$Q@})tcwjgVw`DOK0U?sfM@jr z3h6We_!@OzYatIF)yG6uHwp$`zxnQluYWVcqT<~y#&^uU^ZLvz#qKMsaAXm zFMwu^pmSF8oDimS5r!YmsK9;Ky$&W$A2-?D2afZ%Ki1 zVpBjNgBTn_?yzWFBerC=P|ZmY&QJ?H`vMqFic2ui(Q+b@^9eTjEA%Ub&Z~cja+30J zWSYps!XhB><~P`(!8rGH01|^K(@DbdV&g#}@&p-5d`L>&6d-&pEA3Y3$faP64&gCx zRl{LQ_`#hW8oN*R7#*7?r4_e1VUE2D6cf*byN+gr&2gM_MPW_wOU%liI3{9Ci60-W zG*ga=SP$Y+4s8WJdNBX}q9F^?YXbm**<{xWH{Z?{q>tl_;uAwv2q>-mrTw@q0<1ku} zmrzv+OE)hGz9kjbG-o4cd}gbu-R87fyZ{06{E-WRA6s1Kyls4Qa^fu}IDHckFlNZ> z5v_Fkl1gVUsdWC5N*6Dwbor7>Z(maB-AgK6y`)ktdr7U~i)xKZY9T5~XM2?HCuOUm z2zei4+j93Dn0#_oJ$aaBF@*2u^S^Q6DV^RWbCGR+okd?vLsKQp1&t78JXaz#2XAA< zen3`nwh=~S_+C&_7qJ?#Ju;5;?EQ&E$`}@iuVF{L@A33J9h6viw#2Djj9Z>}^p@N@ z1m`?wr;k2W@hfb4Y+tYhQwO5R4qwX5p{GFTgd};56?qN}=;t|}hZOFGe7Ba57UoF) zh`fc~Z2POG5B`NyE2^~3E}emLBnRD#T^q}g-iv`T(&Q+PyxD9G1`JSkY)^eJxVKK7b%5&$Kj~$7Y!C87Q-F9_{9%|lL802sQ1a9uLj11)u z(zjp`(-7FMUUzRsRtFtr!G}-VI7X8D!xRaCwK9$;M;^BEXHTL4L_2#1eJP@ovkRey165N!N?-ft}Z`^d4OMXQMQ zaWyoLJFk%9JP?x=o0Vd0iw8t^p2E-e2FCY%gMiQF;?WQIyh}pa@t$cYJKQr3Wk-9a zq3mGKG?X3dnTE1MJ=0Kjlw=ypj*(15*>Rp}C_9ca4P{4prlFi5&jPS&^gAJ*1=!?$ zDGs95VEK6eXzjeM!Ybky!i2&X!i1t1!i0hs!h~WM!h}K>!h|9h!h`}B!i2*W!i2&W z!i1t0!i0hr!bHR@0#t?Sh=@fng#wmE#cQ4Oxq^E|ZzLRVw0Fg}xXo%c1(=W9*a?vx z0C$>uD^SWGur=}OS_udiI{fd1PNL$VYQIJ3i;H_9Ccc(_G}Z3XYK*7to=NMT}fCQJfa27O-2a`m0?& z7cXwbEFgHj<$0q#HDG_oT2@?d?t&dJf<@~T$|j6T%TLTy?eXLiZh7%D$0qt%Zrfdh zroqlSxT7N8Ht9Hd0k$Nv`Qy`07=hX(lh%_CTrwb5xBOBz5rqk6g2UBulEmP^#7|Ge z>&`ZOlu4#_jtLEX8LmUD@N|}G_KuWPh0xGX94K)Gr+bEpvE!r=KF$t3Y!>H96j6Tx zt+&NZkif)7Qpyqtv6sZ-airQ}!2`UP3Cq=7dSB}>5&Zfba0Kj!f6}@vI{ybntBEaE zqCt4g>s`KWkOnrb6Ld#FZ|F?J3pz`js)W%@>@I}APDF%AuEa1VPg@+EAP^HYiWHkn z?cNs}0?C77+Zj+py_3fm@OIP7t3azi*fYcqMUHt zL2*KJO1A_ojvAK&Pa>X&IUj+Dn=X8D-aBSh>mQX>il?|=P#_UEpuA6%brDZaN6cQ_s<5VPAUuei-+Z(DG~@-c))-;`h;wiRBpK`nRn+zLyR8Y* zWD6T4R5Kj6IxfgDMd&mwK=+HVMtmK0CX8H~HHI=a@Ue;t7qgpAMDMfh2;P|nq@_tL zr?F*=Gr?^+N?+$yF2{-MqJhxy^374?RbzARm;3E!y^dDB`z_W@?q;-pa@q72NrT}b zuxGF!(mqOTAO)E;9^&+VeiwH^^BJ5x(LJON5%Rzudd-V!86hyy`vsND7@@1rSJ-cd z;pu!c-~o1ZQZ+D}q`C@KLvdAd3=~yW-g(?{e3iNvv)FcDQ%pItZLW_1!bXzJOB!E1 z0qu6!GE)t?%d%#cP_cSr0hUQ2LvW`I0`c`KouQL3Y5;-b;{`6jP@o6tKf-g5=S1@@ z-bW79pTZy<|70}pw^BV}P0AN*%GQI+G#Zr*l#wR@IA+Mzi@dTL_MvaFgQUh)hh-go zbdstiy!g7-en&42r{ZK_zsNWj6VgI`)fR~>s%AUntg5WzjSPrb7*EPgRW%Lh%zy$x zEXC~)4Ksn%2K1?Bo~rhA%jTHZ6ssOQzc{1yphwA=%ZGGrU3+IMLZwm89A<4n+07HfbOW2+&e7=I=LLgX3^^(U_?u;7q6@qTkD_=F9bV8#J1a>#8%{ssRcb z(V&YJ%;*I?V!mTN0g=Y^kNpC3eLNedQF$O~Ty&oX>Sgs%j4uL~#Q9l9%m$-Ns!ERL zXDjkG;vM8Wgcv^?%(ZF-#QZDlhQLSUMKXV$e`Q*$fG!?B)8+DHGzB7zgfr)}fsBSK zP~2Sy|Kk-x0jJ^`2$3}fR2EKDmUTjVq(ZBFFah{$=BnN&+aMVjK6_P-HOKY zDIrTnxak=wqH`Q3IVu~ zUD6HFci!%*&X-j3H=kns0t_Y8C)zElRoE~j4hktIt=_bTR?hJqTlj5OagjqqQHuQni6`Ca-mmVk6_2h4etBHi z`cUKwx0W|vR{%5rIsghf5xY3q3XR1B^i7IVr@R`L1hvb> zZioGcIO+(NDREE)#vwD_eo6VXyjUK6q^>wD0aJwA#Ywttpaj{m{kx3d2epW;UaY>z z9Zu0109kW@rzM?TL0<1vYC6@q6r2hdDT7lXBV}+ZY@`fMg^rZLsqm39I2A%t2B*SE z%HUK`Mj4zA@hAnXj8R3Olwwrj9v)yyfTaj9Wsg5l+Czd4UM(6v6 ziau#KAVPI>J+>(V&7aUNMIym3)f+{UKp89i!Q@?If*z2e+df98^*HjEDu@Bgm6O8b_YMoUflnOmJn6L`m1!$*IT_qcT3Y zpge!2ktvb9|DetzTB!tI?5Y!|IHq=-=^?yP@}jQp5IkJQ3F@~cg0k5&1h3Ph@7kew z9Hcb}Pw)f-pQ~*k5dbCu%lD5Gw$k=d!j|qHHC(cJxPC+$wMQgr&gaPBk9^XUu!-Xk z_!^(}@e~`W#hSowvvYB7$@QGUa1~zBfC+JOoY&__(FpzVIQyFG7}&rzQ`QS*aV&;Z z_DeaWvS7*~l?_u4sjQfCNM*;ALn=$A98%db<&etuD2KGeV~aVj%#?DRcBpJIPHjz< zBX#ocmSC+;s)`}vFl+~_Z&fJ@)z#YAbpfB5MX)}NMX)|YE!Z+L!+lVS$Ph9`VhEWc zFxV9;E{el1>*9)>*UFlrp$K5qU`2plm(YL>IIBqqThclzIBpaO;d;k?PI5Rz)73sM zR{ElLsWg?qIzv?g>kO6)8gu2QXNs~Gdq0Q#2~xHY*U2M0_g-s+OBI5&o^b4I-xz5|I5H^pO6JV z-)|Oy%R?G85gN$O!Dnt)*hNH36-*YXv}{+Z#(nTB&Y#O}+y$qYvimS1Bv+7SLqv)4 z(I(SF(T}1ydXF8K#F_H|vj!$HOgKn{2Ux>z7dLdIlH6Q^-HHV0reat|N>-Pk)nX3B zgMyFL=*afzqVVTAPA9^Y4Lt)a1dNHG3UCL_R=2p5M==u4p(mv^@vE_mmc@5I{!7Jlt6fSMAB{zn$Fbjjv3er)`6koe=G#&9R zam`jF85I0XB6&|w8|l(~GvDIgw_E{02@X~R>=)u4Gl`f5B}JcJTeM4EDB9AxBoWd_ zU{!_M(w|kdjunoo%gjY=N>C(Yh`CMG!Am~q6rb>r!a_vHd_}%vBu}JE;xF$O7K1x7 zD35}QQVJ>npHdjc{)YbD$#3rb10)t{;W=p@nzc|vKtpu;J`yl@&D1*UExS?V%>gl1ld z3yU1fcN`VNrsxjVt1$742_!v36$CtboZ_~bvG98yNf|Qhj)e=_2fNi{k%s#z+lrG? zKcm8ohbA+5G3;r%nIrul#0C+SN9wY`h#=6{N~AxHwsiVK=Kk^3awXI+(#&`b46EM3 zv(fMSo|1F^BIaJDA4s8nh_>l?b7%I;*`SRlbW$jGhe5(Hcf6cxqyX+>$zKtdOGIrVbJqBZ!WsEhl zgcQjVNywfg*^-20i6jXXlBPnoN)nRtf4%NG^ZCr&&pGGbzW?8OJkH0*z0dnP@AE$U zx%YENXV$3RBk^u%Y-FH!@omw2d7$3DVGr-fKb=@1zGu@W_Fc*0CP|m9xrsV;_uu~aB5nnyNk)HJYC__@59(i} z!dU+waP#yG6be}U#uSwU_m0sWDmCxeD&pYMbL8NGf$!f)-NR1PU!#)ZCxP~FwJa;O(UtVhil~kCRyFMZ>NEgUpUN`)uZVp6H{Mplllf? zm)0+Q#6M!nLB;prkQ}5%_u#X{fJq(uCz}4ThIfbl^UFlU&pxp!_kSa1&(qXu+G?-8 zQw@jMeeD0Go_JOsirmi?^%T2DcvmL(j(oEv-}+tbh)7)a8fmX}ykf$9U9$bIY}q(c zw=x_JnD^flw-|+GqTG{5B@IoxE8(y~d3~zse22TtwxJJ^f8@|^5#p|2;5`(_FDH_Q z;WcHf)oB}W+6P~;pNgsbQ2b2+>p|6hdYdYuf9R4g z4nfZt7dSQcdM#fkr9alcZ@S=*l;Ej2`-O|C_d!NVSM%M?sV}SW{Ssf=B=1I|Li=Z`zGMv8Nn`95v=E+6CMQJ7^sZ~d_aP3q{-p+Bf zPm#$~FYtX};?AYg4YF6_J0*;qJ?mqpT)5P7cH`2x)D4cQs3oxF~|QtBx){pPhO0OxSYTI{;$Jv&!)a*@kot?y9E?UT@{ZUFl-Er zu%D=MKH=k-oe~{Bj9Zh6r-78L{amb@>i_?mN&Y-bwffRJL%Uu|E2eCwcv{JR_Sa4> zJ)vs$sWi3?#(44v`>p@ITI#OWIzr5IeKWOTzNJlSU*;bpJIjU*2J3Ebdv#oGjl?5` z|6^sZC-;DK67JbTWykZpVhaepMN%^V+%Rshq#p|8ABht~tNk8RQL@S>P<>DT@9Gjc}cLt>CpAD&rS1@T_rDQ*5C#E{` zT}^$4J8Pnm_!KOK{5P+#sDE0le6FYLh*N$NEef6GZ-k;Lemj5|5~<-qFX_=vDQ zXM};=byB~4Qc)N_H>Cc))v>tuW!!#Kuz!g8-?p~II)-x-@oq5L%cgt3g8c|6>qA9sG``;cKF*U(y0=E zmcVJ>Sp3m7QFO@zl7@{DzuS`?hrL6lcs#Cisu}((dJY@*wA)p-8wYYsmy2lENmaVd zf46&!VjnEok$_tXDU;A&l$r>C<*9zyJ9*&HaYOBQUx}C4IJdCu-_R?efGsP54zpjS zDs}}mFRjMUOYQkL{&b`Gxd{pu_ND#liT?*B{8QNWi@8DbK9IdoNDs^3M768iME*H$ zJ?htOocb5G<(~}b*$W#ifxjK$d+^Yl&`XUJf9}&+Z`lpY*U5nzu6dhQno}CTCH2CN zD5j9krM|tY7C(eY`&M5y}0v6NC!W}wXQOgqOuN0FgX z+5O0Wy5N7f;(xj1f4b&>yXZe(B}eZ6;x=4jPaFN$*x!=GJp-{xgg-`sNrAYf7r>#> z;o$(;?FEo{v(?ku$%{xyUg~&eNc_A|v}^JH?eujd%^wa_23<*58fTE!a-kam;WvUp_wy@AXUn zP=+m{KIPNm;9MatT{bXKClAEm47T4XktQ#S<35={XN(+&#p=@mB7uj~YB($rrRsNs z+&5*Zs81=h>Q)FWS3A4D6aMn?A6Mkne^Q;cI9~~=sa=lq?VnT|<3FCx7I9c@ia4jW zr2UgdL(pvuiI0;3hNb(z^LWL6eNzwF|EBU4`}I>E?;XI8!Rx89hPQh13m+B{->{!+ zl`9vME|jkrSGjW43RSDdr8^vx`riuGQjaG@jUHJcu6$hgu@x(HZ@}L`c4z&-5LXs2 zBDA8)rCuU0&?%RXLyDDSV#=3)BCf&{RY502HP7&Tq}A`o7F`=YV)cu$>`*o_33tz6 zyA1R2Z|R8{QvWOdjmm75wSSKp+b;D)#7N891MbjG!Dh#acq?NA4Am+(M7sB9Gjw9=XLHxz{{$OFVL~d*qgSw#mvIv>EP>s5*ej`iwd02f%VnC_(jPMz*mkK8XFINJAn0H?O^Rsg5A?~X_Az6Xx> z{Tsjq+DGo82abNWZjtr})x3mUh(|8M1IH9I25^D-V7iY4aB4qi@yO-yz|p?k0i4>t zJOP~Az5*V(LLNBUS2TcA+gB`rQ`=X{BUjD?NBb%UaBBN11#oKns(R#Vdf;eZd;q7m zuWkURwy&W_?nw_E?Q0RhsqJeSz^UzP>yhi|funt0130yP-2ynZeLX#LeLQfquYUli zwr@ZHr?zj1M{c+Wj`k%7aBBNT1#oKn#(Cr>df;f^lmJd`-_!t3ZQl%!+>0K$IUc$B z9=Syxxg{RCJ#w8ra@{?02_CsbkK6!{+z^l4aF1NFM{cY~Zh}W{vPW*3M{cG^Znj5m zu1D?_kKAI9+)|I+3Xj|xkKB5X+(wVwW{=!b36I=q z6_@^YqZNT^#u;!ak`2QD{m|Pu-V#i`NWq2T|M8i5KQy^dZ2J~n@n1V|o|p_~tmHT|67wN&Pp0Rlf|=pu z6!y&pvtDv0`*wrb9Obajp;rO+{0Js{w4YPu_a?YTU@B+yb9R0mdX2yh0JA9#r_u|7 zBT~Rze8jBR0eU$f6%4-GL8aFodV|4zlu5ARAXWBd1{az+$jXt$pRP*p2DrvxDrWO@ zDm~sm?+fOR$ie=Ij`WNGx;WAQ%`O5>jl1%s?I zhzvgejb%gkgXB7ZnIbvTb~_qrgS^xZ6OfOEl1V*g1;ts;cFuky%i|)=R8Hj=*8AMx zA`1!D*UlaFp2urQ#-w)xC*#4DE^OALhkAhdT5_aS_H{;nuYsvo#H=?M*C$H`U+tjM z%Lu*i!CfzE)|(B5{KbN-w-6b0_U(h-9xxM22u@e8&O+k=n588Jr>j>@c)C=O)ufEz zO!ev(m;z-5XR23?z$8hIv|X`?b%(eOXSVtE(@uPza6_jFeNB z?>(F>2CiUvvtAW&ZNSWwTrmExvM(3xTL)%u1)--akDtJWRCKp*8n~Y;1zCeDo9){J zdzOLeTE)+)>{|o9*r{FP z;2P8nvewiRTt;vz`!IA^`D&y6#+&up;QAS0zLA`+Jn|vkKfz?FC-ijX!G63Gn46Ld z)7Uo_dZp_l9}UcU;oz=;`MsgwGHLX3!SLrB1zB%ADY$IleE#!y&lbo1r~}i)$(i8h zg3HxiX!29#mzl^%X)xQL5}dA|>;vvHm?wJ*j{Ut#kNw6UU|#Lz&~xNBC$9fgGWco- zF&|IAu*ioq-+Sc#_P{lP-ebLmZ6Z~cTcDqZ<77`2=kRkea9@DCBU#o{m0vjD&E6-- zisgh1LD#>$fpdevME3RjPo>BHr3RR7{rsG&{yhr4OOj!_461hC7#;T?;HnQ6dYQqg z(hUK34$OsNg3E2|$#zb08Cu}qGsAPNs>vm zPgQOepf?TNj&VYd{hBIWw#&@p(axU{oUgxf__;W)FEl|gsp+cpF5+ZUaAhYt^5M`M z4=w@Bxip+gkK=E~XM?QblgxUBz;y*PVY1*%`TZKq_^E=^<+nTRy8@=cb7sBSNOw7y zlG6p3ZoZr9DEym(cpA+18G?Hp|5xRAJ5K-me2}&EMZuK-=Nr%QJvDIt-F&uSQje+h zxSr_waokbqVGG3i3CwS=2`(=o#1rytEJGZ@@9~AYQ5lm?ak4zao$7+M1f*LS%yVxGPM42&aQ%B=dT%uAaa}w9a8(^xw=TBEv z?{7hG5SZ>e1gD$-ZHHdi`$5*Ooq~G=*QxZlPOkYukTnvKK{r3(ylEbomAlOL^@7GJ zFqJ+O96we5<2cgaS&!p+0WfX%n)Pm=Von1y{Gi|lYV2zb?!_Y* zFFzMtDR3(Pu{zEC0_Ap6aJ4jgnc>(ar-Q5w-w3X-MsF>SN1P3^-a%y0_2XAR+?Im!Nz-0VRaJqi70ZtzU^Jt3Txc*h;cM^`T0aN$~!THvWj(rC92R*@j^rPT> z{g7i`!gWQNpRk@mWZzZ*uJKK*(*6+`5pmx9?Y66A|ImM0@p{($9^y=l4Cwp^=~tdUjwt`szXmv zUv`2yn2t+*4$5C1tS>);xg$BBpB4Fyx`uTbB7-i!2XU@1m=f3hoT~kDKGhgZZ^`NM zdj{MPFb!@9JzaZ?1veVZ63LNP`ENIlZvr#u7qfjFk7k1TNOC&8j4=EZnD2iz+t(0! zS$+$$IwCUY{AWQg8BFp`hn}K**MZq8IZ?0F<$D6mMafZB)gKhW@q1wM|1Q#HdFc9s zVqmJI=K|Uh`-8e*no7=>uA)Ea0cO0DQ`EmXU_O(a&VOugSHWz#CGsKs64>5YU%mkI zhvZ1B>dQVH5C0>`I*iDm(_{Hw0#ov~pHu0ve4Bu2@~7Z*^?5J!#)GMPM{v4*$C}XF z4CcmNvmV#+1?~k|jSv}FzAF2;Zfp&v=3fpyNBv{JF%-L2}D7))^&j^j}UFfAqLvrkd~dV_gGa;EyX2h8-pg?+mE z!v1eHn7xuSmB$e<+aCx$U46*~y~~opS39W6_X^JZ3-0niW<9QpbNwqAp{A}coWGO> zSMs5|UU$j3=_P?1YPU2z698B>esH{8AsLe%^Sc+^x(qA{yfcQqF9;@6CbQlcaCO1#l$=iQHn^|B9OXh2 zLDznp;M@Z+4YCN1y@$@e&S08kb=MmYX20Z2dS8S2B%4{U5$w4KrgnC-eavrLFpY9J z^c>@51h}!1!B;!j_n92y44N zaBaXmD>+@g;{L{bFpFZ`^^Sn~Q*tJ~$j5`NM+&*?)d4d^awfgWV5Su|>s3I0cYwJf zIg{Q4GDXaKWuaFAOoHT0ddXnM7B%ZNhTaA+ze`T1$BG_WEZ7Py?ygq_%(Ify>DlEA zW>E>V-cs0i1WaN{!Rem+;(qZFFxg8BPPe~Cy{TXdlyTRa3g&oOv)&5m)hrimokwIa z*%u!dY|Soj)@ufh`(PSY5FGnWRr}?9_h~Q#D+*56|8ai$x@7Rx4l2F#IP(Fxg_X>D z>`xC%MyRRnWB+m)+{q`*dNJTaDhtM>$NlzLaFwc<^=Mx|$(Z!mKTiiYu&Pg zn_iZ>=>O`u>s67An_d@iE$W-~3PWU)WZd*tfm_tTUGIow-1M%4`@W%BkL@i>Bf*&T zQgAX3T;|4RJVR7<8GN;as{W zYbrRCef7ZRi4(9pxeomL) z55WA@!E9e5^y+jBwpJrD=CB{`iQ$D_kwwk4SLxPNd7O!r=bGwF>7)33K#&)(kvlcA4UkL$OB zV8Rp4dbF<^n9Y*Y*+;$OV0QF1>-7hB1x%}cX8SlFe;Uk){_c9K!9)#k*DDUD^gy%T z4%pWNO!h&7%ZUH0>Pu_bHyh0P!GhDx? z&aAf@_T(KOZ0$v4pr2Ltu{=sY6KqYG;OA8R0rjqdd1<1bQ|Yyb-rrzaP4aUpy*Ht^ z2TbB*Kc~{;^GGMbRG;eSbb9N-w0h3Z>Gbx1DKg#9sr2^4z6D@9%n%%(Gg9esz4{H9 z!ZXc!yuUwBGWco-m0k?=j)VL41)--~f0P9m^J1{oa+ct9zw6ix?jo31UNYO)6DPm;w-Fh1_9Z~?cQ9?1x$AudruK4oy=h=BzG2pT z6*je95o}#WWZ<~3^I!dy!Pcx*?s|WLnY-GoNB>;|Q+cgf?{(O-227oGW8{s*eXteumb+eeFuOOH_2OY)$+v^8L5K|eRQZqd$(dlLZ4{iY{boQuc7S>R9kU+$ z)33mkc-O4Q`EEBbA4yI(UdBQ1XE2X#G3&)c?@2H(NRG5BzZ`$x1oOsLv)*ZNUxHb? zO>jEB;;`=knCsikdZ(b5|Gi-ACL)9GdA-t@2$$XwY<1o#INkH2_n~nP%;^sVr)$5v zVPBqI!PeIw3QjkFJ^{VB-C*_zPG{dz=+y*sX0KUq6u1f>1zT_K6I?FntI8tC$om@*CpZ-a(bqbNemtV*4_-f)@?!y=d5gAyo?B^{U_j@|S&lACX z^{L==2ty9e&;X?+*e={P6#dr>8kR(8Qh95gRR!51*iMHP##?0^ee&O zs~uE&oR24hYyP!hb?uk;Q=XNKP*dB-^~^$WQ@%0lvESGR=G`-b)6Jigk>6rxgRR%U z60KEeAxMQly7Mq-vH)o$w6LOzO8Wl8klO|2|aQu|FM5*0cPR_ zf4aWxR3Lb;B@{g z42}60G2cLB@b#w(|9uB0@{-_m{yPAT{9sZfr?W2+0u3(X{@@j%XR@#D)nIEBqMLoo zzoqZPk{2iF@t_eL|{hJA{!*y^s1gGmydB3dZFTvJ4LI?5Tz6<7!Uj?UY zZ`Hwl2`2hC!7(2yJ?5hzn1VOWdhFNQfO$!BI{Pw1?`<$2{%+Q53cX(>gRgeb*;nOO zuvO&`cfEmHJ5%H^Cg>4}lSM{$qRlQ!+M)50!n~533Uz zV$~0G*Bd1nH@%O*9SAq;vAz8XrecKP_^Gmw<9;_Vog&@!UI24gawfgsz+8_q>#@D% zj}Ea0ATsFm=)bvOUdd?In*xnbz%+hDaJu^38G6rw`Swx4>FP7v+mo3>tgVO)y81%@ zod9zzv%7u&f|-=X-M$aOWXoo@kL_)WWN06Qs(f$Y_%?8FWH;N#`)^-LhFT0NJ&u<* z!KLId>!pCpnlr?@g~$-YsCd5_=aUt3g;?wI2(Ab?)qJfQj_1l3VzohJ(3J=CI}A*> z{AT-N!My}#i{y0UIrR>aD`3`RfAtfXOaR@Fixfk-Au{OZ ze@$?%FPL{Erz_v=u(PJpz;rBc z*6RqpnP4tRPG?_f=sf^ayrNmJH}txK*`0<{l}7{U{R*bS6YhG6U_MF1>GbY_saeIW zHwWns1@oY);JEHl>2ZCuu3Cr{RnyO@`m2-B+YV+%Z9k{dYXH5tIw4kaUBUV4i{p9Q z8L;P$WboAvs^@LXLVao^2Uo zjc+eF?oZf$apZ&dy%IX4w@6IURQ{_AzvM_j`{*U~kbPyk`@pRO)31-gg&R{{hT%&k8-=eVi4@$9G^RP8J;ZV^#g+ z0UXaZ1@#n>K{wBt4TGvrMSnj{aJu{!hQ{#eA-H!cIDYE#`yrU0C8x{pN4WlPFuP`$ z?c@G)f#*Z4(J#2$7xN<4gNO_!`v!w~RdPD}*v{Vuvuc*mMw^_Pa!gx?3)f|i{wo99Rjm^uGv1W18UF1dTW8ZecfNdIttOvzBj>~lAO-I5fJ$q zOy`9{58aZ|f819d4QAaU!TH7+NB`0f*WUos_Eo{@`rQ<8bHEH;EI8lz;n3qg!wN9h zBu83RzGrYe>ucZ;8FYHIFW-_7Ybqjx$-cc{mcMS+qkSj9e(LP22PV%lvtCYI z-yBSD$+6s2dYs3O2J_-_vmX1C-C#bE94=GprQrApFq_|S*ZUF74au4G?tuAvg;|gH zg(6l824C%<%5MtJ6ap8t%3ZILWQ3YNzb(O)U2WFO50L~g6D6lB4?ZV756tv6X1(Iz z-j|HYKJLSP5AN@^X1#cblvyVjlU^+Jo(4DXO|u^JdjL#|I?U2HiBFFwpnj73_1>`_&{>ko&_^Ua?*bR@2y}vTnuKViyr;_ z9+)pZaF@VjIOr%}g?%}|6p@_HKKivBm`YC0QEu!PYl9gqIo&)l1>6f@1|D*hn?sM! zW4;dNjO28B`@r1+v*VMrcnh zoX&row@(GL=v%WM^LqqLp7Un=9)K$grsQ{Ky^`R%O9o%jtA$aaaaJu#z5Bq)wQ|!9nbmdzDdR@Rwm7GqG<+}pR_8Vrs0nob)rsXe!)76*m z(0dWg=ml?uScU&E>(Rar zV4B<(oNk|n4e=otcfn-)*R1yy^csT6^H6ZQ@n|>nHiH?Gfh`U9C=pfteG-o63<|Yo zA~NXuXYNxk1(OmiI9>bw0UB9DLajd#890AamD?HUEeH*@28RnyXWvv@pCcmFdKHm@ z^+%=0`u8)KZz2W9PgQgVhh74h6)|Q#u76L08SuDSF9CWx!Q?6| zINkh;&#TOk48GbyRUW*5{t>wAv1a>Dpdscj5^DWYS#ZaosM2cj}PS8R0EN zpa0T4zro)#ebF)0YT8L~Tu0h^j{dnLj_;QYzS_a=pB-FroXOQ$Xh{s|roKNh^@!MS zj`zS(sgVbc>*;0zT;Sh3=?-q52QD7mFCIA7w=!Lv`9->p{evOko&dK}vTWzJUmX2Q z9FB)}O|PdaUyeslfNRrDu>ACuFTSS+u2-J}vsrSw@q_cVgJ8~dH|ude8TAy#BSZ#W z`<)BDbzmy@6r8SqUImT0U@9aC&NTlU3g&1phkcIv$9?n%lA(PJDnD~xowK*lvLSq^ z{LJ}F8E{4VIMQ{L8`l+`z)X>x&d=0a2AAyPPD>$8A4E$UWOr3saJ?OBRf%OJB} zM>Nb?V17svTpwJg%5N@I%#TK6UNT8=eZl$KQJQ|T6^?)U9Qyldg7b|tj{4Ub+yKeo zs~uGRWC@(v7r+JflWaFvRh*-LVSTv;uJ&|cAKQ_t-an4x<0XTyc2N0+_4IXc3uXvC ze){|(F0((I!x4wTWO-R|{c%*KHv}EtV+%09S|+$*Y4y_hnb)6P8EVC^5*)`>ThHNV zjz^m$gRgc_`MEgGgsc`?5)*!L_%9JBOM{y$SS}v?kR07mZ5}{1SaBb!BJnO$L+1Q8$+$4 zTLj1Fh<*7@Q@$MEx^E4&W+O82)7Ep8FZ=Pmk|D>Snn!Scd&UFDb^K)&=cosz!R6Wp zKO-`*A5yiWI4lr4gPFNYa6Q4P{4y2|vG(3jYsW#swXyZ&zHJ)66vgqB!=YB#5y5fw zsPao!94{*we6@qB-g7*f8NdbhFI@j^RdEWx><9O&WOaV23B90CMILP9@S*aHl>vSM zSNo`7`RS`KsTbnkpAd(GY4@3*Q|WO%JsnKl&jsgOzd7m^=UvZBCN&vV`SN$s^T5qN zF7$Nsl8fMugV}n*k*;HYPzQ*d4sEhK}lc2N15``!b=jr+>1Hx%64U~+#WIPM>)^b+8R17HfD6P#~d z>nJz2j}IgxtW&p3KA-qKxQgEjJzqO=+?S+Zx`7#a-jNSSy49h#Rx+k^c^~|H2dBy} z$Ax>~Zhz-U*HK@d09W)vsMQUTL04b6uQCiw&+pB8y}```^M~Yg^_lya>_$-xYA`^4W5_3N>}x(McW zdQN%2@n10EKl=TrvXAvXFPJJ$&QTt`PuC30Ov&lm+YxZ6IURQ2x)&g=lU{F0-59r>l+3CRdGb^E2>6>ujmoAo&U z2K^jrRYGLY`7Z_M+JdQY#jIBuTq2kylGFK*?PC*|`zaCsd~+9Uj!;iQ*yfeUIh0j{%F=ML7dWHYRJ}V-FPS1kg3@|eu z3eGpLbM#lN&&R;T*-x^X^;&{ynSmpqzP+`?xyfLTOHSuM?wkDx=1!1VkNZu(vD}0?by)>GarsKLNAp5wl)#a4BFS9u*wiuB?9?FSCLP%4F8#`o5B6@YN2g^5yfL z?Z7q8Y}Ts+ZXB4+k~7)23(STrX1yHHJ1-gAIDDw=qyK&fcQ&h8Z!x&&Y+=@FLFOW#ej=CKtjGPj40*z=PKXS;@@0R}3(Ua0W<8D{uY*aEoKBDR z`5KsW`OJEyz-7!Y7<{#Z%73)43b;B2-1SCC#!YW2xHSdMdfOrLHJBG;1jlhuW#3HL zcLGf3LV^?fn1Sn$8n}Kdm|rBPD_`a}L*XzhGS+M#%cF*5@YN0~`(mIs8Qh8@{(PwF z^B5>x2J=)=!RhSV480Lx9xpC9-F?_u(0dt7o)Ur!!T(kEB}1ng}_FcpE55Qb5 zB{+Vn^!7ooQ0Xx1$1-NU9=N`E*)VGcB7<%|#QD`4Fh|M>PS+pwhQ@s`-Q(Q##)Da2 z-mLc&^iF^&Q^8%Y8JJ-e-St+2`B!qL{N}3^X2m~Y)+-77lE5sLoUXob{M`&@V`X=} z3t%Fu2+pLJ4@|DAX1$syx29lbR1=(TKa9WUjH@1o-;4_`8|?M9k2L$moUbL-3bPg> zGWhDVV&8TTn5&XwKJ0xE$9Tl~!9S9r9)qer^S*PD+F@4RI_CVwvKc~_G>EpVqo7x zFa^8&Ia|-MKbaZVw~-9>7*zG1>xy0uPL(eA1;&9J@suN7M?2#4daJ?Y=;7y7ddx>- zF#UV_IXfSY_RD;{2Ieiv`P#38<9_TOFrT_``M`Y#<}WAb$OrxMNP@5pU+tjE2m8|^ z;2!Vg$cIBO7F;_pVR+1v zv^4B1*gwo_hRERSPaXA&?Qo=IY!DxIy>gTX^+tnRC0Sp&IsDQL+zv1q28eWZeu+bV zbAV|$P;k0&pX2lxFsCKw%ZKCs2mSm2%!5G=Jx6|TL9gs!!QiVM?E8(5e6+=xm?7yo zRegy^x=G-+KJ7@?q1O<47r~SmDmY#L7X!V+wBkuYvhSawfk#1oLpXS&#Lv z{D?5Cev;sH^{+Jw^bnXFqx_t%{=Ew3faHAkIm&~_uS*7B?VziFzk$m;TBNJ1fAn8D zFs;T2j{S(L-1xiv!(eKRchAQjFjpn#%ZH+UWO*jcs)fj)vo8VmwE?qbg5Y%fqGKUY zYhsx7-XwSXs!R^E`XDlx?3)GVeaY$UqyIhwbA5`?Gx@L2RMfv|?)G(=9%fBPbhGar zFlQyFv+o7i_Y0WO&kH@yCspOkd1s}WVb;zU-R-MBE6nPLn9e>$J)I*N8^niQPZivJ z6{n~#i@@!dtS-MlAf2zlygysmr`xBg3W2;YVISmW_xyeZCc|7ueiinW0MkKoI{WgX zJO+Z9Hc#m3>T@ClGS3gQl3#JR?+%zk3mx`3{9G1(X)2j?<)O&OSa543tIJ0;PV5A8 z;8l?>=M}1YbspTei%}l03y$j^UH{i>X_)mAB7?78I`YAF&wG-wL44@?zcb)MmI*yw zKDZB;OERf?s(f%CvmCg>%Y~k9{N*~ngJev4-BAy=fy=bQtjF=UE|`id1;_cI%D&Uk zI|b&=)q?Z++2KFh-DZtoQteZaxIZ5G7!U6DT4AHEznY5z z-MTK!s`r-Q(%lDh%t7sxt_rJ_WT0Pk>23q_$0z1=xvy8`uwbaipvrH49B<{| zROxcRWC*yiM;z%o%54txHh`)6sW~6hpqBvV;!(lr_OI%o<1YCb?rVH2xYwYsvhNZN$#IG$bez_Fd*_rP)f9sQSkemU=Y%mc^rEaHJ=pM zKkT94((R+CnQxRvx|#7;fNCN#_{I-MzsBcE6D4DV_^|smM?R=G7Tg-i>gHjEkdK{U z&Senk>hi&PXWO7~>&FnmndY5|q2bnCL=tfxllMcg+LGa>|g)Js&aP9#wIUa%%vtEVv}eiuqb#`{2A}jYCgWZoCin zF}N>t3mb)A;C=gws35_4!mY~r1Xl$5D!tR-*5?nmCd3GiezNOj-*Z~h5d%fcxINEpG1IPK$1rHp@ zncq~LW4;@N`f?B4{lcOyuzyw6tFahZ_s51?)r$*`ep2->EphxFm_{W8$NbxRj&{U# z)KoBgCC7eJHNWRN_A;2VC50Zxca?p|aJ)H~T&4V+&pw<>4V&?$GMLMfBdyZghjv-I zbhve+jLVUTgR0yb;Y?R>D`oH41{+eX$L^XP`(98M+`pL>>y^^?o0GRob)7f_e+&Xeq%zBl;9Rzbv zaymWkCuXb)t!ieyNaUjem>Sgu$N9O+e|(?YVKBk91gC446>_!0f1N)}#Nf zfcd?i;P^bI%72xR!RPCTTOT$QoXLOV8iiXM5gAPWy8tGtvEX$63x|CLz!YgBI9>VX zgCn+p`MjCgKHi6&)jZt#4w1pv&K>2;dYZdMxYYuYfqqt%??Tu&m`p2wx+?#jKnC}M znbTHqeE!zg52cxRvi>Es3%3>^GMM~#2+Z%2)A{c>>}l5HbKxNgf-&h`!O6nlqI;S3c7dw}=C0&8 zKT`Rx2`Wgp-r?5jM4`v|k*a(P<9Lg{;nr|O2Gcmb7|cP*>FVDdXq*9ayr0nH{6J-2 z3@BgYrzZ~EcA5#+YEtIV4fQ)I9>n8`C8p! zm~S95=-My)mv_PZBspFC=mIXo@NlauA_F;<|JXjpf=M3XPuEwzyikTo_;($cB1wYt zwO>bn!1pls0`o$$;B@;DtS@)LxO_@44uaD`sLIuwyXmtVdQ zpevZ97ajIF*2x@y*GR?|#D^-s)Y}d2^enR;=h(oqa5iMqrX&GVAg0R;>YZ zX^!AH4yx=sf`ndvIo#?rUvRqqp7kZif^e%IB7?8JIp&khM>3cdl4Cwp^T`c3z8y@D zSA-rvRrc|BENj4&UL?45{WGpf4ZQ!h1WbX&g45-f^XDUAF1{`}-TajO{f?#K)>%Xb zUw@^je;Jku#s=|WKNsonO9GBR2ks}yig{h&K4C3zIhJF+g~*^A2P+}J!@;b1LvTVb zaG#L%^lQo3g7{GR`67=03hv|zcfF96>GP}7vtVC7aQRl5^;m8#!JJ$zI9<7wh9f4d zLBIB<;7sKzIbC^FMLxEIxwav#p1M5py)78q zKzykD$9Z-oa8);&^*GNN1m>#bbn~2eG{hqBgj;>z6?*B~QJQrb>-{M(cO<9tAKQ6> z&EZy4L&2D4dmOjlL^VxV^r%*7AQdhB0v?iLKb+CisR7F^3cW8=)d#ej(ujfkLCL>m}fs1oUVKmP`{K1@Gj`O8|6pWjFb-}gx$!s5=W1J)z zlOFri^Wb7Hnf0Q;bp(@eS#VJhRh7pTa0kI`ydt=C<8P`V_%{!@JXgc5O4rPK7r`wC zGvS82-Zd~Gzq;!U0Mq=YS+6Y8-38{#@9uidZ-rZNx6OL{p|>1N!X3fs=70N8ucGdT zTSx!$bE@`M9eT<4(eM84PghkQ7WDEx!2I-IKc~{;?|WVZv-F|hbmhzbfN#COks-n^ zO zkNs8k$0Dr1bDQ$8$nOO(WeW=~U43?}7nXwgNOHdM(y>nF@t-A= znh$$lz_Bjl{O>xr$6`ggFav%LT$gcvPzp@mB7$RisPeG}e(41!w3y&@evZNQRlszT z9Me_laUM1p%%tLGy^^?o3z%;ur_o zkI10QFY8|tm>E^f_SJyKPRZDU_)wJx_b2}V7hTO=ua;!o^u~glTivYpCq(vxdB29> z9?{s>9ge70Gs0R}TW}oTeEqJYAF5L)!s?C4;42SB|MDW39g?G;RsBnG96u@<>M^MN z$NF~*+=Y0NuC9M604{S~!BC4qr&ktSo_c1zSX|#6%y7x+>dQTF)4)uqZ`RucZWEa9 z4Fsp_PuHWqtOC=tk>L0og|9sDJvE%gmlQA`G!|S&{9lz{K5tRJNrct6so+fIo2glZ zRRNK~RK8uo%#fU}e7UYzA{ko{A1eE<;P|`XUgZn!5p?Ct`}?0t#s=}B(zD>d6mTgm z%zAwOAZyDAD-n@FSH8ueHx|r_R%X3Wa3{fh(^_!4d17mDQ`$sWU$%AVImUDPFSwmx zYyZD z1SY(*KfkK}U?;e|U`|O+r}sFx-@#<<;!jtlcM)71nCx8zr|YkZgKH!ie6@p0kNwq9 za6`M9_2Qwh0!)hJbpGSI=U*^UPdW4y^{OVACXy3=QP-<}V8%<%RIlcMSuQzUy<)$( z9?S=l^Yt5!ddhnB1DJm#r%RVymL3sS8AJvh$9i8EOdHAR?BhJC8<+u-^ZCV5|E7bR z0VcYqNLM$%--(18gL#-BIMX`s>0X%EAu`Y}c70Kl+fFbCCFirx(chQG^*@4nw71zW z>^BO5sqEw&`DMS>6wGAF>FVDDaLd6g=_AtBwU4aej!6bz?VxHODL8Wh+}=d99{ZP@ zV9N9roX)<3;F^KS)X&eU=2x-c%7AGjIo2zcecN%oFPPl@&3d%23Yg?HoJ#KqxcOkZ z4lwJ{zOi7IO3q~8W-y5Z&3Y`~sbF@d;dJ(W4QBZuvtASE?FJJv*w5+o3W2$nhEtUX z=W7u|;1@&&U4FSf9|~s4(}L60zk1L(2IkSBW6IURO?XgGmHZl zF<$8D(xso{z?^?ZaJqUB1HD2MBCL9d3_8Ei&mF-Gn&`-fqu-^Umx9?VIh`Ks%SkZ% zpEc`o-{Q7p@YN2gdKClv%1y#Nak9JK6v?>h?FM&#idpYZh&(zq!U}#)aJqeq;c&!s zFn>-LoM~PA#tirck-=BKit+Lqn4HfGj(%3v7p}WYf(f1}INiFt0R)}_^U;g$_6?jB zVa-NlFxj^qOp4@m_Hq4oTQasFK2+t)`Vu)C>k32$UBAI~cd3^IV}tlm>G64*df*z( zG3$Md>&Jqr_Ojq~_ZQf37JykWPjIGrQ-=8wRxBce$$!nkjFp_uf2E)?8_fG@I8}aA zzZ9)KyaNN~)DN-qV+Yk|rCs#&iR zxK?1ElblYE`FX`CbK+ajD=;KWn2|uy^P4fe5mrv=Y~H9^UrEOr>cLfFXh%mSbY!~n665Xzn2^X zX2Dv)nd-|~FwNEpPFG)eKmK_zgVzhrRA0`5c_2BH|6<>Yuv#Og=0nxrv%Vxs#s=}B z$}i`gGr(=#AoO(g<#Swr5zLmi{rOPoZH8XKjo2T0$E;TYdKorFSQQZ&bn714*9OeQ zcg=c7q46%5ahnCF8{cT(?_gGLHS2AF-YGB>w+T*ne>xd@KY;1*o>^}#^qvP(dWYb2 z^@a1b0bsK46r8U9#X+wtm=`6dtIsjeTMlOL2WI=aLGLD*X1n~Hs(c4PZz7mWANn~} zdwU#ukL`}Isv;QB0up?c^ z`r~!zJpl9S5y9!o;|BCjf%)jD;5cvh)#o(#5%c1B-Oo@@5gB}bam>d%KCnd*z zT;*r_<-TO_)efrqoPsl1k40Fw5E*p+GxcIW7mN+!L#4-kuA1Pg9yjaJe}lk;ejzxW z|8B#7b-_IGrQl5ddmqe6$(j6j6HJqnLeJ#C;gT`sm-G9X;AWpP=a=NETMM&KHK zZ`Nb~JQhr`6v65IHxqh&z+C^qtXC9z`G3TI4kCjtzcJA31!l@mW%F zu755Ky@p^$NKU86`OtJQ3vZhBxbJuvOupX*r_*D5ivyE*%dEE++#)bFZVQg{1yy^C zgJZu16aAOqbo*BvFW&=mLUO+L;TSJxgZmZC`1?YS?NT*fHplTDV1oV@oKH_tpJTuz zJ`kL)Jh=W?1m=k3bb9pPWiWaEHS2MG)ErFihl0z9|Ev7B8+!A=w8+2}w|+cl`A!0J zT5`Jb<@)yunAl*mUN7i%1oM6xPG#S4=v@WVCDg3!%vwC_nUqa>%Z&w}0yU|z^+)}wtNN=8_xZf_CLdjKvZms#&LL@I-c z$}KpyVpV=K!|<+P2IUo;ZoH)4W-!z8x$B(RHHL zZ!MVdh28azf;m^jtjGJDF-0S-Bt!;X`F2Km%m8ztm|3qiG-8WKTFHoRdNaYKlrZb9 zhCxM3M&g-e!4-n2s(g>2A?7a|XAFB`cx8ny7BEzB(xAr%_@S^<@W^gJGW}2l~7Y~rurr2BfmsqSxOD4F8Y07X{F|?Cq;&$3 zfqqfxaXb&I7im?l@8|6Db?le0-yR?t>M^Lw?Qa|(3T|Knp~p{Ey2s#%17KcmEVy*} zNVCtt`jVwdq*V)%!Q|(@VCG3q=V#it7R-t#&GzkszD|sfzZbz|ZttGos$dc& zr^|17*f$o;TWL65e)oVm)WJQ!cfs81XwL6J7}UHI^3l~jzw^QDm7J;mT?Ui2o6ytc zw=D8o6ioXxoG!luzzpwh&M);=fjJ>LpM8#bQ#|U+Jup>!2tD069Sgl@!K{*;ZXU*d zV;`8?J2>)Q5UX7@4c(Jv>#WbG?B zUBAoc?a& zD_{nA zv3Y`P2hLZ%X~v_zINtY_Nb4vfLlmN|=NK>fJC?Ew1w)R(9xokSMjUUh;vDm)?%+Cu z+b&tocU9vr^Bb}V^N3~U{2oJoQ&wQTy3swq*WQh^T5WdZS7F~aFqyUpPG=wg@*R>+5{Ja)seKkSOMmK_pY3p>Aof7gTM@x~FXXFheeZvdEsk~7(t{b-~$`GncN z82E3|sYq+b1$X;){t$`1QFr^MfcZ{xI{Q9HJ1X;2q;>z2&?}_zUkDmb%bz2yw$}vb zdta5K{;@yKd|fd3Y6n%ldKzc4gPS5*UAyG|{TpEN{vtGW{Sfof3ryLY?)jMdd!#kv zwtGGr{OO(#rrQKuj(hIu4*V<9disHTx~2Z{mToz44<5Rw+d4y()joth3+8x;s`)s} zEo-P?$T6tOjrq+9?x%3Un(9lH$SA8uM)!38l8i`IpYA{4j%IRCw?LLCt5A0LbWwx>?vg-A%EAaZ7hAxJkv_(>+x@ z%KE01d%6otdrS83bFyrnxCT#G91>8`9AWv#8@o^GF--qP(4 zu2LQMbZ5p#S+ncAr`xoFd%7G4S~@t@czF|CM{tcA3QgU7hwW$kM!ILk&mYt>%F5YF==sXSG5(f>UK7dS zs~uGRIMYo6_nKsN>E=SZ?}PcGwIf}}^C0v?hBkr`sjB^Q8z&>d{UcdlJ`{T8+Pdo% zhkaGR^=xOhkM{%Sfq7qY7{-+4#{K)xz#MLG*5mWfe@O;k?Vzd$v@dgqD648mvtAJ> z3U4HreUr#Wzx|sFozdc}nlAKP@g5F&)ce|SP z=)V%(1cR@3(D^SO+<@+8y)jT&0w&8-eom#=1oqVjGpmQ6Q?+yc?r9I0KP0Cs57y@_ zJ)^A91hd`^+rRl0hnEBIF%mvL2iI4o@mzN{o1ZzW=l?& z-}B%$fSJ)(=#f+TkH2eM1LlV`oXWlwa1X(J-_Kp|A28MX`#GInXE0R<_&Jq*@kqBL zm_=zgm0k?|w;jysfkIEW{$PKaXHb+?7m-2NKIp#=V9F0R+qWL+wgPi^h~T(iqOz|6 z3Si~aQP!p5f@8b#%4&(opz{mw=k*3tIazSJdNm$gdoaf($8=SG;dAxYs3Z42Zum!BABa^^V#Rv zkL7bgS;nLOJtOpV^|S{Je;&-GiGs@qeU*KQD1d*Sjk0=86nyU!n0K+gRgc_*~j^5J#ZDKr_~F5?{9W+iINd&>iqIP`&e)j zW|;Lj&)FmylinGe+yQRR^Jcv&;P!#JD>>GCRerDFc#fHvHz6|U>dSJREB8W_^*JH~ z>w!wIGxD45#VBj=EWxF_UuIt^KDgihi)2y{tMur8kR}`TZC$vtIG1>+8pDOT-8Ju`eZ)dRV2$_HiEEv4#G0ReEoO`xnexiv-8r zB$ZwS>6IURC=t>v%$?>X3j77oA*mb$|>#R{h2JwqpZi@FzYpiNIS{6=`92I z&I+?0pZoe=GA2EiZ~m20R*_X^J@!|fz|4`HuD@b^-Uw#jYN4kakDdeaI& z?Yn~0_0OxoO#@SOvste@?5hrD(H6n!>L1@TdmhZMTLs5YRep29@V48ctU>PyE?s`p z%qQ6&+yGN!hv0B~O6WQ2Rb3n(BN=?PgKC~#6K8gUyDeGPGrRsd%8l!aeD7mEj>tg! zRQcfh*2nLRvM%frobP@B^&I(F`eBrH9g%_a9$U{*PuY%Y?8Z2V$iVWo?Q`UpTu;f6 zW3V}gUUM8D2JS=2(tj$B?c*+(;609X9e!B@?lCYQN=`Sv@qTiSy%?wWx%(yfW7Jbb z2AyA+k5yoPm7Fgh4*O!kmE4c@5h4Tqrz*Et)YINzW*iWlu6=OdXD65wlGEvNy>Jms z_CxM^jllGfoKBDPjU+I=KQZf-0QU-*J(APurGWbq%$CDuJ+23T0`t@n!TH*oW4^Hl z`F#yct)qf7tq0!*b6j$4Z+3Y&>NESbJ75ZYCiM8Js;8xKd@7htl4E}T?IZnr+V+F_ z+Q~WkarXDuz=RzW=^|Uy+vj}m8{1oEFfr-5fcLqvpC}2YisU$+tL$UC9l(r`oGu^a zrh{4O|=YK0cMtybMyz?cU%bOGs)@ZS8Q)r!JIuV^5N?b9QBm_Q}h>6RyITi zUw&;aef=v2riPPq*vIzU3d~r^(J!k0itDYZU`C&Cw{Ja|&5|?OcM!}OC+Dz_`;Irj zl=xDltMlJMaE-x4pY(I8^%3VyCBQV0oG!m9;97zyd&-ethka$hC4hNJayt8{_coYW zr_FlI?;bGsC8yKd2QJrF@E;-rIaPi;;P@Rd&At{K%R^VbJ;Aj5#%$j>Tt6Mmzmn7G zT?Ci)Oq6v8k-=A=9sc7!b@8)NR#!v@o!&j18w#f1IkVniaErkNe=9g&fA1JSIR2Ib z)9SoK&(YqdLT`#>@YN3XyiSpi;P0ZWClDD-`FIjc-3yL%9eT{iNH7;9XUa#|_fb~K z6tf=JSKTE;`xsR9fPNkcZtxFgy#e5sfeHOlaJq5268u~j%&4EtdbICN$>6IURQ6?r z-d=D=FPime-vcm-m(2Dxhu#V>H!hp?*e(nF9A$MwWYD!s_Cw>q)Vkv5RPFLcIV!<^8Gf;B6_I3l@L~_5G^{Rn; zT{8G;2bFynapp9*oj1*TtS{HV6#HFpI{P@T)&NuPmRYYMxL#n^NlsTD?AH#0x$=iu zuNd_5+(tiy$e`2XzE4juBmXq(aa>(18Cwt^D*uH*Zy&gWcg%XHaQz)HpWYQ5=RGPt z<~R2}w70*^dhg@&=7(T5H_$GFAz-&r#FY6n$2Uyp>kgA2*Po&|fFh$=s~M+MmgCMZ;J zrv17rV4}kWr|X9jkWNJ~3nVA{4fXxTLtw5-PS=jkL+>9jXTwFhtk0@`m*sXB%!mlV zFE z%Pm-;7kIys_ZO;4hFT0NJ?^)+1=lr?S&yxDwq$HEjouj?Uj%MwUb9|2xD{YhB*%KC zvXA#uZiC5~U+C$|m-ly7s|-?ID=F1&^$~5p?D8 z4$jR1Q{!>L>GuCPPh1FQWg)X3^Lq+R%ff=wwT~vy8x3Y}tl&)k3n>z9O+sWa`ELuD z&x@M%SpTvW6O1j050(Fz-!kB;75AsB>JPY|JyH zyOddv_qSe=jLAOUe>(~8$I@oKVi3t*2K5Dzf#ZV8f3&X)m=R^odc4228qAGyg45mK ziayiZaWOuNd0W51;GOFjs^ z59U-=!Et}lSFh5HgREB_szqCC5E*>y8Am@}6X#OE z57!^eGfvL2KbapIFN4`AIb@6JD))hRfcYdn7qCCcaykL#JIS%$+xc+V$MO4bFpt%7 z_n#`ewa>(EAfij|PI%)j!U|mVo)Bp;>P*^zMP_(@1c>`sb+6e7R1{)y}mZM}rZpsUZUFQ0;m>}0kt z4jMJU43?ZOzie-lz|87w*5mqo518aGg450GCPVKyn3mlHr_1k5=$!!5xx3(W`E3uq zSHN6(%4{F^bMo|vwmKm)==#5dI5!GRwVr;?Hy+tnijRi)rxTc^lGD{c?#KQ=_TB`( zs^W?tp9Bc;B|ri}K@lGq6l5_8$i5^%5R@QW+zLKkk{9wI$%}7cv0{S}B}Ck+wzjp^ z*4nDIbzfQ+6x`6(rS3(=r3wZWEh;LN-}igw&VBn!B>DIM`+q+F3pZ!pnKRp&Gv}O{ zJ9q9j&^)v(b^Vy%r=S__4wsuqUO3(sfM#t)>ip<8-3FSMEV^XvrJuMLG+nDw=g0M- zu@()2awV!Cd>$MvDhK~qt!>5{DvQ9s`Wnzxsy&X4k!QvCzCUOH@DllP7^@fnU?{uRT5@{7L#(kAkMvqO-?G z0za%zAWKcd&5L>{kw19?@QXmRE`lym{V0D|g68^QxE*%=Sb%kH0^ReVIct@sOQv6z zp+eVy=F2sjj(ROoet!Y}w6$64d)&P2_Qv=Jdr^?MyOm#lBQet!YYFPl==uMG8m7c`?T(saq}$9%M7I%uxikUBs5Yp;W* zOWbLIr%HEWvmbIkL zkL|q~G`%iOUB8*g?^w_*-<&!>w)b|>d}h%ls~`P?E|+Dg8JDNdkI$W(LG!jnmrS0i zpEIt2e}ARUkAD-%^I5>(0Ge+sx@7ArXClAhS7oWDpX&VV`A5t=kNe>7wP+BOE0Nw{ zJKhJ~_^Wk(soJp&G?!k}c00ZW&7y0=^-H9OmY^LQK=ba;!gPuFLjBz5=U8vQPSYjR zLr){WlR?w#mzpk_-dKu`F9*$C*K0b?e-qX3CeTg&HT263sq4pmX?Ztdef+nYj(-#7 zcQx|63pBHSuj%+4B2jz!-rZi%xNp{U$@+tG&4w3STqRAl_P$CawXzlG4h)Ty7RWQo!_4kNt5L=+-`)I=@UP zvfDs&_e+}2hfEX6-!dr1!(Pi$-nTRz*V{y&#@JQ*?eDx3UBB?Uqp7hzw- z{RSMJtrWvEpSNAV*T2YCGrwxPe&>CituFk&?fSi*-b0N)j226&Wcr!<@6TC1RG*%j zE|q@1yjKtPU2jd7N>P z@M2eirV+;APa?mB>velUcc4(yCYwibJ$F=5+xhJR-B{2K9Nl()^DUZG_2a&lGeI|d zOzQlguGD!JO{)C5qP^=ux8bDJ`H>IzTQsTiV|yP0-A!Xt=eHhouY%@`ahi_xP1GMj z)bD!GkM;WpXil4yx_%AF?;6l_o01^E{I!)< zRn)Nvj04Tt5p;>_w+D2Wf@aOsg!xsO{OGsd3Yskz9o)Qx{Cf`gw?T93G@T#m#Q%%g z50VFb?A55SUtq?<#rbE>;IfrmTpxKFc|B+4hvutdg?*MW>&i2Me-$)G7VC1MZgjaZ zbhCjU1)5cD(DC`;CeZw~9d!Qy&G+q~>s``AO~=hES^Ze<>7co|9dy3{&GvTCy$zb4 z)7vh0FlZLGgRUAh*S3T1R?xiJ4!Tc4GjK-g`qh9hMWFdb8+2np_h-<&*ABX`Ky$*( z)a5e2si5(V=DE z6go$iEe8F`j9u#-S=}(P!SAW`20X~Ta^mXB8n@pSlyJ4rzo^pJfQVVX_OcVSRc+H+ zJ>v>gjcTZ>Kt)iY#q*sb-SzI;wdZ+IXOG|S^ZPvwK7SDP1CvpuIfH*N2fg)`wV^7{ z;6qC64%XIHS1xI19*bt48d!3Mx4z1^8e%qXR6$9nq&!mv{FQ3ds3icluvRuSC@?OG ztVIfWG^Zie94BvJ!1Pw#bDtiF2Vly*#v5=2Jhj#8aNxEBhu8mnRi2OD3- zEduYZ^QfaN`t&e9xVFKAL{%O&(2DF56Ito2tMk+cRevkKYfOBD*UqQx}<_qglS5Y;0$NG`|~ z2v$v;SnI2F*Lo&S^djt|6mG{r*6v^^;PR~$ zz7WUa1Xm4R0G0(^)xJ=DmFkD;Fh1SHdwnh$4eDqsri+R3g@P_$wX4ok=ku?%Gwy1l zz4bwlA0x_2?zED3Gx4E%41C}6dhdCjDp%0GT>6&i;k)CV3Y-=-Hc6-iVf9Oz=#Y`*}2bf8@gP}Ykd{yJ;*Vn z4{ajHn6gV`O2{ni5IR3Q_vYA?RUSJnyMk%EMy9Rs`D#7xdSO;}Z&ayKHNs$80_AFO zFGrOj9Wsna;8d~E?pp8idg+j1D1kIx8+RT&crcc(!6(w_s&fZcsu31xPF!{zm3D)x zH1$Y}Ix~)XmAf_+M-J9e$MuBw&C$;)b&pFaoh5ZS3XD5ZbRKzL6{@RSEBZ_NDnHo} zx6Gs-nu+IuYYyVU5RAabn7dr ztn}beq#hP3O)d1et5i?xXSzDW?}Y$RXEQ|?iPpHGsQ>x47K*NazPH{R^rDN-^Z4^o z4*7Bw?moyz*N=5sRN`WFn0dGrK%ybks^NwJr`tjmtZOc0R^{13V$&3`Qh(FJbYgUM znWJukMWoS6>c$6AOmn;RN3Ca#Hb1NxQE7@O&$;lFL{$-&E;wpWjW@}=2p&#WK(V$Y z?JB6{Q1MWUGMI?%W)r%6ek&8|7RF^nrU*xyGP+B2Lv2Vik0~+*Rp+e-j!h-*FpUe< z2E7fnYlY85<2_@vCRFeB)!S9f65J|yV17@vI>O2zTd;7%>RlCnS}gYCS>P2-1+(fQ z1eLYEfXAYss$fh{i8(jq3qr+Ps)D`GxLy)huQiY1agHDZweA3wpC-%|T$L|W0Yjyu zXxy1cZ;6ts1<)^kQ$E!%dXk!nEsIwmGGqNWoR;&BPGh?s|I_8$6oN5b=oP4m0pdjJ_N6V zeiCuoA9BH>g+v9i$BEbekZo!xfJAFg$km{2963O&_J3R&X>!n>kIUFtwv)8l=b=ev zbBNLYj;qR3S?l(@gA^u{hxU0`J8C4h_$8|5}RpZR|iZR$tcF;STUYJrMtn>HN-m&-dAgQ&WB@^E=Cud3@d&0 zt6br3H}Q=ozTW4ri-@Q6GrdaFO^xJ^sgg>!;$qknwDPR-RJ$=`MOQMf)m=1wwGn{s zaBT42eto5{3I^Ea4h4O}W8%}o_)xHVykdojPmjVEj&;>)tIoJCv2idO>=qRvfdXZwMORba!d!X!)_Q%UZY&;=ZXL;zy!pn89b)3543pUUv!-jEb%EYF?UlhbNPy`y{5ETxbEGmd9hrqADa=wEMs?Lu;@<|=)>U9MQ0*{^RaoPy z@-FvkQ6w%$lWRh{jPVs)1Am@1V>VAhwc*83Oy+zIFk;d@#HAUW8?z274IrzP`In~ zWQoYb3^EX^2*O4N#Wpeq$1v-b*BJv$fnp3cw&|EE?A;bA;0A-2SH=Mmw! zN(`!?cq~lRFC2mSE)_Ge{li$9Im>L@Rba;-nJ~T(Q)-?5sBpYJPj`j5ngLS@V`;TVhQ1$LY>E-qZwpl}>|34JcJb>=uY93wN>fNQlk zSYu~;Y&aSTgKlqqK;|t>njemXTwC?2@IVoJY(5N0MB}cee3>D4Tq2SpJNo!U(WC7G zhDJn-n+6+GzZ?_Za9<2dMpB4pJDTGsgh{BxWlo}1EAlZ!!;=#gijpN$Mudql((B=m zVmKN-H8LD&CbwoZj0#6ViP;1D#4tMO4{4LjVhY0eYIkiw`%jE03`f+uG4rOmA-*V# zw+3Wbaz@9+71|nPOgJtOsuQ!u>`n@!t80Bw+op+Q!x5DpZ!IVPW|WQ#M~Qh8L&nU; zOGF^(X|UTp!A80KK8qh*4}_)QdZ4==6Vf1x<&Y*G)*=#5y)0{nESvE#91?G@z)$s7KI|Fg~u0W!+`7WF=>vvCIi#1tS z;aDh!%-rLcl|?-R3*@y}usy9}g{Lw&m&--yx_+Q~7K>MU@zPsg?c)?M1moyO%uFX& zC1pJm%7O_B3X3KS2E(e#2Zve~C0uapB$`WvBk1{zz*3F`oMMoiCq(`@ZbWpZt_pCW zuxKiFTV&=A$I&Db4Wze{wZVF8MW}kRwVtBWvJ4lR>kS4>#8l^0Y0e+vaY_c34$=#w z{gL%{6XS<@@?sT9M-?IJoI$3gC3-0hvddcO#=2%`wd?V_@StJM+Nl^$nYsJ(tTYiz zx-h$$xj)G@naweL(JfR%5&Kv8mQp{c0FNI{ti^*OG$T|Wz&e?y$_ds@g(}O;z0Krf zEz$Y>lUMn?RZ}tGGIJZT94*;M>8^^k6j{BljDeDw+hmGK#42?vC{{;v+N)U|u1BB- zPgdgDZP!!c$@N9Z?5CZS*~E!-(6Ll~7Bao4(=v4}l3b-vg?*eB2>YNpF=Mgj4T0cV zc*IAeXwCp4;`LNOM^8ZXC#GhuMcUwDozV!QW?8t;%t9bIRg9$9+oJYElH!@d^fke? z>c`08VGL`jo6E>odxoVMKOC7>*dCnGzmty7K`8tkBX>1e|Ix!kh7o+rD$i6*JTh}b zun?}9)TP$c;Tqe#MBp;%7E4!I7%PUMJOebyjiPVqxWDg!Z~p1a+!L|3O>3GKmAiVS{p zNEt*8ggj*PGWv75r(QG>IPruEF7&`pI^hQ7xW2Vo$dR5jpXRir5q+38`_tV^E8&-S<*7Q2^2u#QFQElCP+ zVGSZUc8sMXTSP5m=t5ip;EzggvS`D*VlcOuSnN%J-P6|hG!${Z5oM!IjS!?_OYFcjgNvhbMnAj3bRvvt zBS~^5b5yzxAz{?0oPS6eM2$*X_X0DV!aC8?(NonF#J_7~D?Yw0wRs|vzuk`Hc6pT8 z*G$)$9-_Sqvol-^(+gDWEwSC&Mj~V5sMsHAOX{#HzkDiYJej$FG^DP1p*oIxJx&it z)I>ATTyuPZipkdba zbjS9I_~*9Pjp$QmYFupT)no&X>i=437+bNppcX~9KVW($M*T}wvyrSoSz1#%l0I1i zcZ|FqX{-bmHe{*RXb9PqK=sJ&8KUF#UWiI;u8>U;m7*R`vE|peOd=$lerM*6GfA~0 zZwr23#O*L6CFn1Nh|KfWs^fva%Csz!OR*Y}6~EOG#;`y*DrRDX(cG9%VQI%wi-VE& znIviTW45VMk!O?8aCkTkA*JF8Lnxy}vze+{M0s7LfpBvwiZ$=m)+&xT1T{7T!*d}f zFDw#C&#}d-Bzpd&+et3}%e2$CD#}0|hq&FQ2jZPdOL+B^Yc(7X>15YgZhyT9>r^Sf z)?}c&Ft#VC3)q4P(S>3XB{Vup$(C-DZw2vF>RF?e`@ zbQ!`rm*~Xk`CzJj{n}ws0JS1Iaa|}~{&hHiZ{h~21$*1um7Y`Ys ziIHBdeCZ=SXt-x>G!iC?iQ1Xi61Jz}bZ-wFa&p@qbCUZ>2eOH>$`qmdjg%S93QNFl zyVaOqYil{p+odbJEL@;xRKHM&qMaZR}>Wdo`gCvb)aKHjvttc&Vy%AM}!= zr?{3j8eum}OxX}5Umt6uji3v=h3sUI-4Q>O?Y!E}qMpcz)7jPNR4zL9!f)C?csbNx zm7)^wNg^$J*#=LIGII|%>c&-u4RLE^*fvF1(9BCsYlN?Cl5q`;Fhg`WY@acPKnubT zNFFt8|JH$QqK0h`)F~u7N;t(dbllh$#W97{3L zdC87Wj-cbgSuBCbzD+&}gR5gGU5l~wxXM#0nxoeA0H@6&z2cG3ozfCqYG!NVRERYhbz zPE@3TSfeON%UFesGYktCUaD)3#ljDpg)zzn^J7qj^$FtPg=+$O9>>KK%)zcV@oW0U z$5p$%wIMt{qt`SgJ{o<8XAzuuY_+=-5<#u6l?jXdt?4AoQeZ}~oEvb-vfYVC*bi3& zTzCC<;(_vmGqXqES;+LYPRlfEEb@`W5k^(JVjM-IN5nrl(U_YGFD7JoUI9gn?GjN= zKmC|>aMAKzd`2=8kBRvDhR+}MAmpS1F=zwPQ?ePu_JG#6m9nT1JL58ClQ;ShJ5hqz z2M^1HoND0+uEOJf?ZVNDAd;5l84TNoU=0jsRb55c+w!zk)=5QDR!HHHPWpn#j;-6+ zR=T2b_G%oIQav^U!x&KgT!C7;X0XLnG&D~qBQO2fNFF!M_Tqt!zp`d+m~T?#m2DO| zMsZp*(9Ui;$Ir)hu0v%+X9y(1j^JMmD-GUJ@{-43%JO$e8* z8Z21l8Ff$OC_)3BU+$x5DyogUXC zn%2${E6xo;>TLE%Nn6xHH7j+$73$S#Zt;rd677}6#-%9i<3xSY4mO6nE1r?EnP94- zU)?pXe8>vbPk1Y%3%*EgvJRu{TiMZ^@sV|ObF{FkWr*A#*A!Eh-lo>Rg5sOQu{Tu~ zyS|Jo-s(q)ro0fudCPy>AeuN4%U=}As*&u6kuGj9)hmHV$MvafIHUfnkBf>LriBGl z)e?~HjpIT4Mg_W4YmoTnTkIc&+;5DNOtUW-I~XwRFoNr_!9bqObD;k&uJ^UFG6Vfb zp!x6+Yn*%>TNNhP0&Q>&qlDJr{v=%{+D+8%`nZ6xJy@Zi+MAnwaU+ncU%Rcs)-@W^|~1mIarEGCwHYzK6a|&c*#9P z0UB@2YrFty1;_ESCUq6K$HfIQD8m>w8kV$E)3U^5_23?pJqZo4t!z*lA4QV#cL_U8 z`y6;5#Bz)1Sn>6T=)ULh#x9zm8jk31;>1ylzHQIVW;-ejZc&cT%a~Q{N)}^B}tK{7n zA9EyVUrfb1@CQpGt{O}VK|UAcnMsF#SR=OO)P;3UMG}$1W5~*t;v9+Ld@oUx+hI6U z=)H(Ot4>u3S)atbUuJj^x}ZIGmR)#3d!mgAAndgMJs3=K&BWNjRuYbOFMhXY>*E+H ztyw0(*#2o2+De#R)UHaQGxJU>M^c(Jhj7X~`Ny(YbRD zyFlu^IQjBK;RI}x6 zz(pEL9yS4E_A9ZkFn6yHCQiI8*>0OKXQ}h%w(2~(Q0T5{i*Afwu4ury3ai}M^9ix$ zMvq_Mi$6SoVd6wOO4{67n_fkg)j|zf@$w3}t$t|N_>k#~s0<<$h_&@p_T}6Kg&3Jj z`0*kio%5w|`#o^1wAs%-gh6~IJTfgdao~+=PQ8Sk4d5<~H@Hg=@%osO#9rf7)c{|V zqXWoy*R6SbR8e%lyE~(c61Q1WjY!-|S)=_du7cLvENt7IQ3_RWr{v=VDpAjPP@8I?NBdTYe7wL7CoBn#mjR}1^ZWiS5iburChBCV_J z#~9!>XY+g~J+fzX{VloZpCoePL{F@rRl$-zKD3aU+>MbE=1znwxB&Jy91+#U zTo+#zm8T=H@WIboWDv9PEc-EJofYbYoBNZlv{Eb4@>pxV zROPfg*gUUI4;P*z?Vr*9b$G!CS{co!*D+k$V3lPr!^SH`Y|)_qE-xJbUT@$?In0D(K6@&gb?Sh{3 zHb39J>uX=)^tYLU|Iy{I&lve~(f2!dY}j+_*tdJ(uz0~g{`1XGzqR7eZ;t9#TDSPw z=gv~<3Bf=3$ia&SuDi#>aXbx1J z;Q#y4%Pa27*z@wLpDkV5Yv8;8R_Z3fKe6iJvW#E?Yn8@*7I*x>P5l7b<0chuDyKJrGGqs`ag&N@~qW(tq^uBCvEj@ z{jVSX{x62zapV3A3P*2h#YdC`f84MBefyT}C6{_958L$Dzl}vl{#@|y4jpi^XZ(n9 z_kP{7w#QeOeS(jK2>#4%w>*9Q+_8UM;V!)H)H6pMiLpQQfCYPvUBUUr8S>5@mCxFcEYEZ-~S;#`X=}bPH*_>zxOZyEdA6C*WLcxMc~c9 z1Yh3s*dIMU%=hgLb>Duv;9tjAD>VQzl#_P%l@I;(4|bZtc={+fB7qCw}CrJaWlL`AWSf_?#!l|KXH5d(J(5 z_#-#}X6!Y`!_UEqj5%qK-~7&VtLi@fxNQICx5q#A6DaU1!MCalH}6_L{H&YQa}U+s z((||Yc!=OX9`xip7p@xjea1fj(t_uwU#!%Jg1>RWWxlF`o8PVfboE1*%i^$>S-6|+?%t+ zIq}~QZ~x1iAL68dDOtn^u5a!3%MU;NdiC7r9^EwXCCDRewRNCx#psWGQY_zzxOn( zUSu<$&(1pG_fL$ys_y+M{}^`6&^b7@^&G($-}G{Y_gA$eZX5hu;e#cY4a3J91ix_i z?Z@qKp6uRnx^H;e$aUkDdQ9+DpB=tv%8mE;eD3kzkDWj7=sWTD8_v|rNxP=kSI=ME za`8XwUVY_+CGY)X2R^iB31x(wT_aWayNfw1%-aSHAyp%dBz!s~Rq=fB1ELR7&uZHXOY0{P`c%Z$0|y->IKH z_6$BMCwTvsr&{vUcFlO@SKIqvHvP&u&_9CTe0a^~8{EY`KfXKTx%*aJ6To5aunR8? z#-B0o`a5LfR~iPZbvcGkh66vXSf!H(2nO(=+0sqqTl zAu|&1=N9su9K3pS$a!?Q64+W*jUz}8xdN3kXgZJ{YO!K@M>?WXHh~^`4KU=qzK&Et z%I`=9Fb%8y^&QD!H7CNOy!Ew*&W1|x0i){BGtlWf(h9vae&{vOi<}*)!5TSF`_O9; z^+w^Lk15$L+)&yz9Yii;o*d-th|VD=&W4RQ7)|QsF7M@9fEzx{Sba? zcF0-at;!Cuuwx!u2xofYI4o~RL}@La|8eG@zqr7e@2O~TP66;XIQe^wbGY*yK>$yL z{t?hW#v^u$^N{0Z>_Il(Tl<8|*u)jhhvj6$0aqYFBZ5o2uewzD5&^QJQx5m)=dPVB~_F>%=! zb>hhAL`HO?F)HjtA8JfNXAD?N4cnsB5xLQMjqe;gW@pxUtsGy}iR>Rg+O{_x=UJzW zE6i3fA+c2$f~>1rX9)3QU-QSJD%4Vy9+y>S7E2jWvx+Sw2CcjPXdfarTtWF3$G#?c(g&MMsL$!Vu9o)1{p-AJthEb;_8~d36eht*lJ<$^N_r*p!^5 zeuSNO3^~*V2%RLj3$2htRbXG+wb=KWrK+uvL%oR53D{GYrCzZ@4wZ%dX~PBA%L+Nv zOzbI}>LlH4D}-aR5t@d5!CA^e-= z!lti7xzczcBiTJFKN!*h9#4)i8X*BN0vn8QGl16O0;zpP)1gInqBLw+3MY!pA?+B% zprPOaE+=mDBSj3h?%}8&h1WuG730QsTbwn)uPW$zvFt|N$Kh_mSJnQ7`y_-}4RA~9 zG6q%ea^T>>zki=PPB)S5BrnX3U2}>zIl&t{D&;0uc#~zU1zTg%l8!jG--(;ekP8IK zXo^A5NTOr}1&Axg-H7daq`}rRoWUI#r99N?;l!A$E-6cYA4>n-e{D9R5}zcfQ%v{`4-? z#)p601wIw!`;|wn`T^+x&h;}C#B>PZ$n18VEx>yz4*B0;(%-@pq zV&`v38ILTJrv4vAbTOAo@6rYTV=p}lDE+u{a#TP2;+OzFFX_b;d2Pg}h)$Ysr$)@t z=N>!lH!)D@_R8@fob@qB)kF5&7!6*?loL{gy-gxtUWAr|qW$^dX{L-W9!xwOcK~^w z2b>eo+UD5!vCA{ z+bb89!r#f@&1ih_1fScs&ytA_8=mpCB`2S>g0{V7B$7R&*PQ6#wa|=1HAM zT|CN8KNJTo80iZ@szYK2JTVkMq>M_84qpGawSACUEhE^2^9jfU+7vge9Iy8BFIf)l zO&=cbL%?pIW1ls|O{Bi}aVP3Gj2wmn> z=i^^ks~M|8{WW}|c+f#rZ6=-^?n z1c_3PMC#))8#!Jq!({Om@vhMiXv6=qtVH|@`z`^rl%G0__F*VN^r&c;1K>-o&_sI4 z+D=Bs(@LI*JKB;)Qv3)!TMC|q{SkA#fKg`cm4PUK2AeN`X!`b+6IuIF;DsOmI7WX+ zUq{X}w5)KHa%Rzfndj_={^A;|Uks1wG4hJG9sGQ|t=??k?@)Q2NPgNHZ|&u;X@?Ul z3r=fsrF0EHn@i;cW9HH3)x%RD@G819=!5ly|IXtmaJ9D_`qI?en)7Dt8*F3`QN|5E za`x5!vfS_;gD8))RF`NY&P=s0r~98$uU@i`9cuKziqxL@}^29hqk3 zvhtW+$zc^(6XsIj7r5y$n2UATi<|W@?ZG^^k-w_|SvO3}8nMQxxWd|9ooR)#u&SzY z4z$fM{a8zING!>Q-$(YtU`tgvt3-f_@`-G zc1v020xKMrZ1(6V-eGy`Lq1AH5%KaK(XT|gc z(@?%>Z+qDzB3Vyhj-@{b+J?YqTODu|`Zgf!C-=wWHW$mKth26c6)y}Ak#(%XH{$dj ze56q|vR>-|$)OLx1KkS@>0V&Sp&qbev(!UY$f2xW2xc^`%T}QyYA!@fqVC9E&{Cec z(CU)RuU|iEOVgG#Tj%sessfaef%B8_MPdmZgOKi-6A{`2T%IaMD0$D!LM*RL4dOIX zGw`J`J;OBY8JwP=zQQ9veWmCbeJ)>WO=L!fbPT?5!QW&pe^=owEv=YDGe!0m>k`{r z>#g3p5b!v}UjoRwqAk)}t>BC9ErxV&G2~FsflA|EutL&X3!W-H@Ek4xpjGQ(Eu{z0 z70smwTFMSgD$5BBKu2{cKhW!u^p>*hE@cShPI^4pwP;Ig@7909Vst6Z>Gg=Rda&i+ zOSe+EVvR#NaKy9Kyf{esB#}58hi7n~64Mn-Lz)G+545{0^f!FiAIv+!)tD~)r%;gp zwKajL{_x{>4*CaCVmahkMLe|ZVh>Yxi#!EobFy;rX!2kPD6e#5L|4uR(p+o{yD|g! zWDB_&UB-Ma0~`W)Ip8F~D**X4>ncFD{ilHB+vj%IGNikfA&2_Fip>&zr*YY7Iz<*v zn$RIVIQQf{!0v7?-P>Hcueo$TI=Z>+z*D9B;i{_jP@n*-4|}R~uWE!eWT1mV+ETW6 z0mMOuE0Q&>+owX^M{n7E8wxgQ#b@oBDheVi{P81Q!IJt`Oz)K@=6N@do03qdt~OdIS{w8kU@y%4GYH>`ovBF$9SH zKxFsEz`^H6@kWmRVj54w<7rp;J`cWv9(J^7BFx1$@5K-92KbYOT$!dC+5B3-L4fsu z95+5dj+t`-&jAbo)&i~q!~srfHQTx>lzQ=`qKULvdSz#*Mc^*7(Xh-5Owf zV#XK*9x{j_*HX5>=`m-^z6A&Sq6l%e_eBk@uA{fKj)t5!J$_8ndpn!U_qLSowH@>3 z(mgF@dt^9uwMN#SmNbg^o^zVEhqL`bxyIt0>02 z7DB8C;+GNYG&K9H7_nxWfw&uSKg52OE1C67Ce|Wq)Zi@MT&5aIITHzV9wd_kFoc_D z>f@93=kdEgT7c=2T1(@UX^Jojg@%YdkwShr5PN|TKNd#^Tf+=iVuF10ku~B$kP)iB zhB)gY4=Co`=Cj1Y{Rb=|U-khW4~R2~8fO6R2P^~p4)83%?*Sz*h~K&R4IVeb4^v$L z8v(llQY@*ov{*8v#gZY18irocxRF)}TbgX#Y#RqZ%~*c%nkjNKMcLEtKLt9apkAMv zE_{caJylHhDn^#D3OZI)j>Jk|Q67_c085dCc}aeNu^zyUz?~5zPE11!N!HxeE^)Fn zA|+_zL^*#~4hv824p5jVC2U-Tln_OQNzG@S{~6KU4Inlb>&WhAJ*zC_c61|uKMTkq z{&zrZM^n!O<^#R}I12DZKstOc0Zs?}C*WMbmjO=)d=-#we+^K0j$y02odZ~RJ44z7 z(70Wo*EqW3d$OM@-KX8V{Thg~*HW4@AKsjq62ZhDXj*qbg(wA!Y}*VI+Oi<0xqN@q zy8SA6TnjYU?7Zf(ecA#yPwz+5O|zcIsImjiv-4Ve!&`(DG*8cw$O96205h<#UfN0) zI8g08#j%pyuwI8)Hd~qwNRc5MDMFv)ro%crMr4?VG%dLO49Jztu(})YP24JdFeYWh zlw{Ib&{8;*k@q1GTr(6Ok>pUKSulE*p>{ z*AuV=FbD7yz}|o?H4l&?cQ{}L;1Pf;0s8?40gnX4Oi>L0{0ZREfExjwfaLigK(eKq zEvgJ@QDw-XdP28oTyHDnP?K$3v5kZHt~gkOtW?#F{5!vC&j8D^zTRBAqq%fvbLj`o zrMqAcVQ7V9^b_+pm+q_j9mzMB?wP;B2@#e70n(PT4;HL&%CDwvkoXBi}r<5%S)^q=K;OJjto5Hz*a{I%|Yk{gz}&V!B7dEhtP=1#7%)R*INh-#hqy(IHaawK(Rg?Qu}a+R5xJda=AP; zHSYSVT8}?}n(cN{{);2OfKXg~R>}O5a@upo@d#*qqM?`v;uaT|=m;yd&tF_ve8w1? zlmn%BbaC<7Sa*mhHdK#8udDbO%UVzH|LLOh3#9FzqRFvyQ1Xj&F~aXK3iXxDp^4&< zxa8!uxr_i{X#)zyp(f_O46!&I(LGeQ;W&v?|D;x7_eConjyYTL(0YALED# z;tV;|VvGr<$WmuoA&0seA)WT;R>-05LWpvlrS7vr4)qv9l=3X~gcWkA7ZBpO$Wkv` zA%_}4S4ePNL}JLHT1r3Q;I>WP)|SQjnVY9)x16$b^wYagJz36DoD|4r;2}trC(dl1 zgpLLN2!t|Z{PYnVSbUK5gS-8-KMGHJF^(K12Qp-?*iS-ib208l+*jd#qdPc>z8#Fa z2={*6mmY@md2!!^dmHYTaqr85QUvRNkNbJt-Jp9`_f+ar+($zDACG$s?h@RKal3J! zi+erp`*82Y%`+u$!hI+1H*x1f0>|R!m!_L=UxWK*+<(LU4(`uzr}f4bZro1XBXLi} z9mM@p-1p&r9`_F1yKz6>2mP0az4^GG#61%Vb209fxT!pUgZn<*{DvjJSAQMu=W!o- z1jYyMGTdk5uEl);?oV;&^~0uL+-KvyxIfO##eE;{3AB&6FT#BtZcbt=VEpgHy&d<5 zxMvK&6b5$%?%qc$E>yOZW+z^)0^8*>kXjPmBYpTe;03sG+CQ-2i?pPnCjA2C>nqLI zfNvGz!;VfKocF{Gb8Xx!6Cs&V3KXJQd8!pFv{8mB|EyLKtdT>p(~#Nq{W06{$I*S!?kQy5Q*v=rzkasMc$r!#$J!jWIZm2fss zYejMn1(~Fq&W&20;xanDY4$jecMlJY`4I)M9eZ&@x2l;I9zJLz+tv#ZgF@v3&IRlP z_#?o+fF8gj0NMV2fE+j{*iylemI{U(Dib9!MZ_)aYb17Uai?+~6F)9Fk@8aDpD@bC0iSbutFTag^qZT3UL~imdiXM= zJBlGaQ`Wd$pw~DVzR70F*%2#m;aT!2GC3X)Jvq*fm>lc*uU_-kljH30Wr+^Hq^JfwD3%pzJSLVEX?T{6 z%Rz^y28~|8F@S!+lK_7VI3AFEo&ZRWjX|GkiDXDiBts6>4GKf!4zofI#WiJ(QLY&)DzGq>=$#5xV-<>}hE(R;wnhdW(P3M3vPyGs^X_Deb zg!E+i0fh8$*@nwwn+zF`>`s%JgnrPS&PFGCJ_csN;be*ry1di9+t z@Io<$GD8m4jF3)?2Tl@ls6QY?S&$|vY(}%0gfRDSMen0 zEUw>KWpu`bcLs9r$b^?1&=cO75{mVR@T?(I9tOAfIRm{N{Rk>{FrN&DvlESs#d%^r{))$2a2`|nC?WA8>jC=FK;jC*p^2wd#O zKBmDUg>vHVW_udRREIZ!Z^{LOwH_B%F#03q3#Ej#{JC-VOh||y=>#4eIW6f8@7ao^}4St zd&4uKLA~xP&sqQN_aF)NMO@w#uIGgu53r)UZ%6-v5VLo|9tYxVi}*+o=OClrh|fbt zjt97?u=~f7qC7Jq%WRV++q(Pg=qwM9$kOouhx%@Meyrt`&+scWm=#_B4ciqWF)I5g z!w!+X-B}hfYI3aAq@zf+;iXU?Hm7O-WYd_PNXEuAeKVQ4mS=+|Nh|IZ((+7D}&vy)2c#0R)I9VSO?UtyELtJSycHX=nsONru}O| zIf%+@`lb$~Xp(9@30bgw;C1=krMT9kO%}*nnqXz!O_C*Erh1<_;>yuL^cU++oyNtF zyKpazu?$Q@8mw`vthNVG2(K9M3v<;Te|yIel~^W9aN@2i5S>OPBtHw~C+jOei{vNP zQ1#EzGSKwCqAP$j7wfziKd`uKKmPj!gNN<-43M*%y@1t#p95Y3_yyoCfd2t}74R#- zH01L&;0VC|fM)`J1GpaWTfjd7{ugi?;P-%dKA_UNsz&lE9gxj96NT!*%8(wc3_0Wp zlg910=&XG+G0SJzm5>>7h#KA90!sYu8p@4Ut6f@yKzENtXS6Qmo`zsIEVZ>RY+9Go zHDHNVDOS^(v1U?sU{mhK^6#*GvT1hO#_d z(7s)WpWm5vfSjmRxUG~9YM(s3_iuFjbUXA%gxK6X#Zi@9%Rh`*iUXO)i&~yNZiMsz z!m-nNrXdY|&7-M05X@2+8whU$^?pTPYd>p(!sJD|*TxpSI zy^hlTEoSLXAgT)G(Kw%;-iTIlca8}3cP3s-KUeaEe6+-VV}2HixYNMb;5HZAPGOJS zWz!A9M}Bk%qyyjpoCMeda3LUegf=b(#OMDSR{$Ok*Z|lM@TY)B0+N?U0kR*KqTjR- zGo*!>A^p5s4!9#FxwWvI&Ik$Cj)4Dvh3R@)dun~+;nD-+C z)q?Gc`x9&jv(}ri*Y>!U@;odZbKAxN^9&37A7!rvH|8R`Irok<>;Q{cA>KL@dF%aO z^AOU~SBp@kNaUq9j#Bm|ZgS0BS0g~#AV+y!kG|^*c_b~><^WVY*6(1NzPLBw?$Nf| zghbYbg6;}9ep)ic?}!vg7G_-#NbZMVYz^wfO^Q|`qS;@EV|A2hYqLBhl|mUOscexg zsZ5M?@=6H&P^&&sF3w^^ACDWC*%#CW6|+jG*Tb!09P_-))-XyerORC84JDRtag~J} z2uUNqjsfHV9t1c9@K`{WI0Ud9@Hjvh;84H-;0b^~1{?|41b8AKd0qfWPE??Owd69S zC6^(G8jN1kxZ|vlo}p?SXQ-NPiA}f6#)*W-Zy$z&O`}UuIsn4i5BvHlsM!eHuUzEq z#=<%@cup6-7m$-4s^5i5!X0_x>o<>jsp;`9&D;)rU{YyL==jmkY%Kd4I`?a2(hr&B zAw&JpMABe~6M)ImL>PQ{IjtJ$<`ET|se z6BbULEgiNC(l0soCf$2AS<^Ukhbm< zzy|?82W0900elVcYrq2J2gz)l5BMD*`|SWAHAs!EK^W2+gdv9-4JwTrXN4SUg^j}o zZi#iM8*JQ7HZEfQM;vwzjuqHn6*bK0p|$n}I$DW{|l>XSuhn zr3~|C(DBF>yQb{N{gg+`j77>2uu>5KGuft)P*}~yx>88cS7CGBO|N`p0J06;0oloQ zAkt=nA>GLgIn*mwY?dtkYTOrTd{o`*gGPXOt3^x0AA;Oy^sE;b-Gp$D!DK5r_E*4U^Eh2u%_# zxC)`k5~3B=`tVAGl8X(^2w9ecn-|4~4vrBUvLw}B)ZrwSt1Yp?5}3F_EG@wWWk@H4 z;@g12Ehof0Ikoi|1&ei!6fEQSSb}99pF5Ea3pLwS`CH3wtms*7h!nlq5mh9Q$OkGf zv&!Rus4-bjz(IhyfXe{Uneqb15r8fDjZbEaNcwG&WJrr7Lk`6qRvLG-6>=zQFpV2y z;~gS(NAFCXn4cuQ>9yojo%u*XLFg}Ach^FCQYh z-z9m|w&DmOzk=1_^|UovV%XZQ&Q9ReKSK>cNbfLZXK~!-VI>^yS8s;+?6O8e8xUG3 zA$ADanx}q&&@jPqVl-Al4tB zo2xroQH&o(;CEqYjfZ|_3qHr)g!rhdfMzVvU|Z-F3zsJd|6X>Y?2KpaS`Y|L<3! zK=;Kr=#M!uZXf$^Jn)3aw(0g6>&PAxJy(pR=E^hym9s%_k>!gxhr`%$yX*rJ#?11E zY?q~$qhv)YLMp}x3rl^<5fp-WTcgKpm$mh2giAKxuB2`=<-9xYnHGKkR2f~dfq)AE z2LZYP2Lsjt4gtIh@OZ%c0EYsyhO03`^kYSiTm4v(A%}Vpu|F2v!&XQyL7cDYz;}kQ zw*?{Qg7-kJkV72{GpF-A-U>NX8A2S7S!#h5a;PN;akOTsWmX9186(7zo~3+N$f2G_ zNSE=P6>?CI?MB6*JGV6LOzU+KThrv(@;#KE;KO@uw^@5_TTVF;x9gU=L2DQ~;5x(y z09vn5!gOc~LQSYuhMJC0AB6H0dhZ~9*cW%?qIyX$ut%{xay3IV4>_12)18YXl)#J> z!;IYXAcZHT8M%k0%0lBF`nEZkC+;zCy2FXxwYhAY=yGFDN_WjSsHSzh)T%i&A)2JQ z^r4orhmb)Ie5XCepdgb6TFQ2{ls~k&>nW2;AAoN(DWPw)xqKV;a9D4!a95kcE3p)p)XT^}5i%^>2)+6+V z;I2Vvis0^M2z{6#8?&cMh~lrkwdW9eT~fS@(7z@0DMD9Dx&J{(8zw#>(PD!=eU@R$ zN9YQnqyKQZgeVNLhG`OF=K@c0<~16nL>eYO#a|a`8=?)K{YdpenBN`)WwT8T6h**X zsSVVhFcuC6=^+^?IUE90K$;%v7~AyFJaLq5#QuR8^JK55lV%ram?*)#kyGLySAS*Q% zkTn<&xDjvy;BNq-xEi+sP6MP?EC%FwYsRS5#*mss8$*U1>TihMEVxIlkV6%tW|s+W zrWJCiJqR(EEVb7PIpnZ^ogYto(fO4lL>-W&&ay%dRfP~WM3$|%!%s^g)vOB{-lGEBi zlE2zOUMnG)`p2&ZbKO+T8y0FX|CiTF=kNhVE?#ACNtf5(@#EO?oM2CwpO#D=<4Xfy&3VbT$=)3Rdadz-PPA63gL;{g=Sjyuw#7kb%!AE2pu z*f*oponWVa74~@v-3gq2K=Ud>Hwf-)8^@O>bqX%TC10@MV5J)2%`0{Rw*>LA>s(C3 z#YDb3{unDMSF!~Q%&96H*1B*8tTz}EO=-$9DJ|F--Wkj%H&q@Sc|(Q8an5HpH8|nc zVVE#JG9o5YXa0tf- zXhPg#PSdvhu=k7&jP}A!+>60{7n^U7ll&kri(VC<)YvEBPyuBkV~#J&VS~@A%Iv65m^$$(B0P|UN`w^Eq1sw~Ac-iAFMwKu+g#+;Ui{z? zthJC+)AT89@N623fSUn06>t_HRRx~Z%4-Ox0y0e*;AMdG0Dle$32uA_5YKcP_W&*i zWVz*lEQe-A4|Rt0P-jT{SCok?MgNK+huV%1B_>P#!wNa%!2o3}OJ0s<$f1I0iOz40 z6>{JNe4hBr(@VqWvrbk{?7T0;-^uvrL})CeCVY0XIS-p>URNM}0Owdorq|!pNR+ca zKii~t;SAJj{4?hg>vRPop~+~b%Bi3Qs7>m;61SnfC%cML>}M%O>kNZfUAW8Ag}v_2 zZF(H92IAbS{llqx_Ci7Sx_?1BHppe+Xdo!B=CWO_4m`;2b^mm@Si7(~(9AP}_R6vW z_m&v-*|e=+lqRBJ--o`-lb3>$yAvBh*AI83E^u1EsSD_!ER9(hWEv-K%K8&OR2PJg zowOEjMH{dR@mJ*AWpM+XHrvuC9N-+aF$3HT)7Sw{i*LW1#_>#Ton$=EkEj*f&G8($ zD2S)+jZWaO0ko0&5zo&XPXas#kmK47SOr)KNPblTa-7oD)8mvOJx&?YuLx<}I`SEOZGOD5t?_7kQKv!hQ35D7$bBe2D z8xeY3V)hAUx_(8_&pGt5Dw{?yx!4lr*1->4wL| zBq|T)^K@*Q#KeAf*SLL?=U5hQ>DFHCCQP(5RDXo@uoz^AD1~3*X6Kvhcq_m-UKjM{ z;0W(}yaBjapOCogJ1{`xWT4nHB1}bKN+agcwy<;OAs=k>x*25~;V;lA`OErI z_#snvTk){2ky%5qWal9SR)mpCL<=KBS{NC^gC9_9+;djQp?2E14{V%xHi@@{U>_b& z`7G_0P$gp>hr@^GAvSa22$rG;Hit|q&y>S~TgPFutP}CFQ!vv%5KBu=e=nGV;aqb$ z@Z!glwg+%<>Ap13aBj8_P;J7&pHUk_$PBGVM~;|9gV^obOmh_OD{-I1fLzI@Mp3IPa?GYaKg5)1(rZZBNHC+J)4~K)mgfD; zDj}rCq-TWB+F)aN)PjmenFST|!Zwfm^m{@9Pr`FP=j&?7U$&Ft)*bgu!2n`~flWr& zH;xCK1$Z*x9Kf>y=K{I`=K+#Grvb9!ToKda$dDFCh8)zw5}QT+Dc7O}mbTTa8t}dA%;V68U4tMX&o<5p^*C8Un~30c5Y&BeT<* zzFE_2!*%>!j+Mi7y?u34CU>qrQvP+*x06ELn7#WHn?xt_Ltv(zs{A2Hd0%@LhZl0Td3-+hB_BEMyZ9TeG zGX5Mo4F%Zk;M)vew_a_dTKyOnSpK9Fh*ul%X2Vw~=&Po0tcu-<^vLw9rjHAnzGcNq z_pjXIAj#&%nJ-}970WFBs%ihEkX`0FmKmFC>w^)f{ak4^i@(_X#gi^DHWzHhN#9t` zmBCH6@OWLjs-QtHG<{naS{jFI2%Q{Takv1=j%clTp20i)#LRhv(2ro||1k-DU&bLE5?9gP;Xu557=v9QY ziP?$JTeveM*Buhtg}?uj&>sAKQ$l!sOT8hXefayjglNTMy_l1Mq=wJL%`0~C|5DUC zcK0^ZaFLpD`XEV$KdHdG^ca8NcmMXC8>?a#y$JyVjs|i&cw~b?0DS@lgR$L z4bTaAJD@y10$hyWcLTCc_X5@d-VaE-{~+MSfDZ#+1^5Wy?*ShLya#X#AlLUE1Nu8db^+WB*d6d*z{3Ey0XhKx4wwzN6R-#1F2G)Zp8)m&?2D1w7cd|2aKMp({Q!#r z`vcAfJPNQ3@EE}B0S5unQ5_6;FJM04(||(&{|-1D@CCpTfE4f75Z0~|L)ukhNS{hU z4VEQu3NqwSe?vlY6<6f|FaPrI< zTkA1nP-!aA7tdbtxX6Cu%WdebePZ4sB2sLM`5X-2Cg22aH>#N@2d953p;F+I&jUCB zF;=4#Ew@Jy_c`vL;*LEVfoVuH4Yy1P2vssgE3Xo)_IvTMPMrK-zgCt(nSJ~fISFur z#M&%Ey+Z+^GO#q1tdA5#>B6=g%4zruy zowoiNri7cY@su=4mEJn7bqYH8v0d3cb~36gb0fD2AaT=s)7Ebna-^T17MB4Y&Y{>Y ziC+?DNAZfZm}Ew(j$|6r$UK3w8@bxmk+r_IPJ=YFxps8qMpHB(>%@-a!q)k?d7X=n zV?D8hMLIA92!BPb0vrgq7Le289|Ll72LDCQgoej28(5(Q#ll<&ScUi|z_oxE0k!~Q ztFBDiE&-%~a0qDuVMq%ILs~#Ijsl`_7{St?y?e0U;r`5y@6X3}XGF^cx-`3K=d>P; zR>~6iiRk05iTn6;Q1MFE$5$h6Ud%HLrXlYZ#$W9pt+clDBQju} z$g9W+_9pP2^}H0Y4476^E>FC;BX26VIEG_t#?Qn+KXYcZdt#g8f4RG1;>3nfK+6R4jlU=p=1KK9rs@EGb1u&-6$h$&s+=N0*{9Ey%g|pKy;OCKe+<%9Q^(% z;7Y)20h#^J0NJ*m1F}fYU$m?+q-BL6ecX`7?X>8yUrw3jVDp92BnvW|t*uiJz^JPY zTY6pe4?xI5ulsU)-FG3vs%fVa4?4|!zx90xhRi{GgiZkV$uvCqwCCf9!R*%&>bW9~ z>kcOK*42pb-^aED1O+#YohxC}W=u)oCS0;lEOiO}~F z%cZjaN{I6JCGLFO<|;yL4t|*OV`q)eA};p8P^RImgs`;DF~CJl&Kk|P!BfBVn|j(9 z6Kh6^%-JJ?}0pK*idjU%Tw*k%o@1~)OIUQ06G_fcL<3M(=5+i46~;(ppc=yHbBb_Lhxcs+i~1!&NHMr&yYjDcBpY%K(BELEhBeb zKru|pgY*L( z(fKIXT&xRwG}0&f6?&5OyB?7C4qebV?*DLiE^t;=W&hv6FpTGn&Y(v{r9#EL8;TbM zF9SM?mr+m$uLvUy4kNcOF zsFeTjZ#`@8*=L_~W`>Jq|2>~Qzu#GV?Y-B2*0Y{-%4}k}TKQ(@Y7E=4w_UQ$D8bur~vX!hFnKp)G&)z|Qlrg%}feY+5d+GE8seT|9W}*l2JmI0jTH ztp-%>U>x{=U@iD(a00jnybx51+J=a-6eVUUN-P!pfUAnjbnt*@Hr-J%opvQ0G3$Mn zZlv8lrecJD*rf{YL=~7e1>C(;&z^6s&9>V>|_|LO5tw=Y$wMgXI7lT z?JU@ij`1%?kI%zR?mBhIBBw$9R@0Ksc^sUl& zrFY3&$Mxu)?z|D)6TAsL0K6GI1iS@20{kj?40tQ3hQ8ZD=_@OPd3_Z#eHBZ&7F6j# z+HGzlW^Jj8Wot_nvr?Dh+e%$xDc6Fkm`?{vD`KhOZJ2Z*9sJd^RIoGJZ12@*JbN#L zh`igN$Sseqmzl1Y&Ac?J>lc}Jk2e@?Fv{RmgW(244F($wG}zBz4};GdY-IZ312^hW zdOHx=SbCcqrMK@y>Fu&8z1`v1vE0$%*h#RP96K#?J3De43!CJwD*wOMu_=*TW8`*O z*TLJ6u*h{e89eWwJn`5uRc6E$@Ire2GZgSVDBWYhA zN6(VWVy?uW(v@(=M%kBW&EyUW@IkJLNpuAsmh2lmH0f|i(&6Bw!@#7&K}m<49>@U+ z({*^__n^e@!xF!fJ(iz{sZ?poB~wtxN>16y45u_w$HBeKP96rzP96@*P7VfTCx?Kt zlShEElShIlf=7WP!K1;mzzT2-I24=!4g)8H$AAssvEY}$N>D92$AQh@@!&#mIQUiY z1W?0jP6B0z`=OjxZV)p&EXMX=T$OELdoa&ZuC$<3m=2!8O)TZ+D=0mtwaKPSnW^9n zm{Mjs_=9IDCmE&Dv|FH5EERMk1t_(qU8{;%D%ch`rQ39%2~}b&u>eyFP6vB>X4?ZR zEvMa94q~aGlB-J9=|KITV$>dBdpb7_+!ad&wJ@dhbTHAgRL}@hnokF_JWFXaW0(5V zZasO~fmEO#QrUsD772GO72FMzMMwwV_be6s7$)104j%O^75ouqa$fCOD%cRYk^j1@ zuEyhPHTl~DQphxZ$gcFjftR^5i)w1gh1hkE{3s9a;(FEU5gU_Sb1gY|lE;Mywxzx1 zI3RQd^=_pLy}v)Fj=Q(A*7Wa#3e&xy^zl$W!t_zh^ij;(1k7!*_h&Ujtzz4&6{n!> zsQgRV?wNlH+w%4$Y;AGlXi%#Hd^(}cxGKvjjw#U^Q?e{v%|DZ?Ita5nl94kX*JPnm z_eeOWaDKl_M&ET;wy%WOajfKBWmZuo12%#)JQNN^R4>kBz2gHuq8dAN3Mzjq10|<&P+w7jU`@TmOufWx)||P$?){~->$kfj9dyUqnm-M1HGgWeH#gUR zQfST?>k7NoRWsci$58WarWuiyf;E!`O^UkM7;~z)X6aSG0BR50vT8_mh9&wmy==n^ z>(x1@rpRy(3>|WI;f0l}ReR=DZaLLVPyed5442LGsx{fKVy)HImg-i$YG=DySe@vLar9ULd%^dpLALAO78mLYrXq+BwNWp1L~u80KWk41j_RC1(obS3u@p)KX5*{JE*|!0p1So z1wI7s3oZfo2mc6u0elxkhv#g^`v-!G>w`hXL?vHj=0eQOg;*;1$X`tdJ@9XCXL^ZtKirUCJJgo2BPbuuA7vAGs+}9OvAcBR9qQ@y_j@$n8h4;m+;3 z$n96K6P%l>;wL)xChR1~{sc3NvJPe;*Z>>h!WR&ar#MywJJqog*h!Ab8lCKznm(({ zANT%)vp;8eWVp&jm$}Jp*|y3++ov-P<$7{hgYJ=53i~NpxT8?UZ)Lp;rcTbO*z3Li zzz}uVcDVT!pzp}CVVW;>j1Sd4_|@;)#P7L@-``IBemeHs>J+Z5 zI^R1BBs*D|Z8$HJ1IH^qwZ96=y1Wja4E`TD5_}V!0=@-K1OEsryZsY*J-8aY4}2SZ z0Q?L13-GVtFF_Ks zk=qH88_ODae`iN-qa(M8k=x|RZEoZ?KXP*gr~FgWC>${=WZv^-a}^NE=WZTJY1t!m z)ZM_x1T0jfN%o~~zCG4Ke@i9UXxesRz^8$nojBJAKUeY`bn{?j6u=NmeSQ<4X<7pyBY=RG9KJe zKefKG{)@58;iKC|^H~f|v89*nbxpI^_hYvA4m9S~@#yT@OZ^ZEd;?#66hj-l_fFH( z11LhqelH#{qJH=G_pE;O-yeOHJ;zQ@%zJ1;MTduGU%0s!Ilg-IEE>c!4|AsTT-Z2N zD&t1OoDVAwuE1@yW4FLAbypvTsko4Ftv|-z!R-p?rq=b7-Sbo^sB%o@ZZpbKID1MR z%*^vN*m>?94Y0Euy99QwV;a4Dj$?CRXFDc!Jkv4$<(TK^xT>V8Q-=zRD*wr^!l`xD zlhX)wkNgM+^o6*ika=#Xy`*6bn_^63Uksb8(b?+K2}7Ouncc^;#M)>7W%`jZKB~df9zSNhU zH~S|U%Z&HoT<+cfnX;*j?}2*v`=HGE2JmF?-=IpzSiU(;KzC4CLlLM#Voy*xN-=m3 zSOP8rw*sF6Q{b~;Dfl~ZYw!(l8}LoA98@;n8&u74TTtJ&J*d`6()64!fPFxj%3Z(< zz`mfwItt~oY(dPj1+kRtolsgzyM?R7Qf>_xrK)tWgnPxTZ%e7b`nJTZZ%e7b`nJTZ zZ%gUH`nJSU!3R7=DI@J_y<({#Md*~4(t+lWh^2zFU_N}FA=b!Esmj8vg0J~g!PnfJ zxliuI%xsRVCcf_yx^2w`m19 zwp^tyVD4*ac7$alVrAty?a#V+P@Ud`xev$lP&3z#Bbe0oZQ!}wqiTJ#hSAbKv>t=s z_9O#>o|~h<>LAmhlFTehSF7*gJ|cXe8z6L*`z*IreDjs!*&6}*t>WttZHDrmHDqSo zl8|QDl)qHCtM|huJJt#t*F$$``iA-ALXHtrO@?oDC*U>;>Juwtss?suV(5_WQKC}W z_Ig<}cN8*WS|-XCy00BS#;nBP!46%aQ!ft>1<;Q8SlYQV9(5CKqbhmNs{1NcG;-!? zcRCbjX#z6dsfe*@=& zlKnhTpRdf+jDnaM1u@&N&D_>|f7U^5ZpuQj2se{_wR0H)D(0KB5ZA)RI%hL1g zN?bo)XsUP^tgr$e%~ir09m+*j@Rpl$(Q7%A3aESJ=RnTuHcbIHW-MEzfKm-9h9WsU z6c#Cl!n>b$Jf4VA_=bc1!4p7*^+X?GO3D^tVisXyHb%|dR^Z><3Ry(A$U;w7_Ec(@ zMLYaTXYcmI-uZo(Wae&gW)AB!-;r(O_O5M~^P$^ZTceB}HuB1+Wf8(Q#B$8um9y6e z{g`&LW8Il~>+rSc%bc5N@7mQZd@;|8 z>3!Gior+1Jy>rXw#EVmE+Zv;%%1E_aJKt_7)s>|?!+bbPr{BMh{SMW8talUW+oj%H zafbI+p~jNODd6tlNN^x{8mPkB>7YU~3cMObVdkjTdJZTVo(JkPWF9OTh*>faOSwd5 zZvXWDQm$5RZhf#(=C8kJX}6+;xg8O?RYd-@HqH#PiAk(7p=IliB(0Q#bbD^n=n#^D-fyJeW|hVnZX7I3I6oque~O@z6@ zIk9hLRr&mr%e!aamzjS?H_eo{8+i?vo3GIwS%&rLayMvLrXdq7B%Fm!tI`SHvRbp_ zrga(DlDe*Y+fmj1Y~NAU{cPV+)%~;`Rb`gX zioHwUv6fTMJlVV7gWl=ez7OsL-U}W9-Uprv-Va^?J^-pn@k3B@co1~Sj_)yjRb|xl zRV<~2cwALA?FQ$YTS5Kl`AO&x%5zO+9~7_1*>|T#PqU}$hUSI2DwT<;)VKNI!YZW# zreZb2sUw+N|1WMymC`*D``0;d`Y%x_siRDs)3AfBOehr_%NZW}E~!}DH2w%x(YHPf zO2r-lrD8t;rDBUfsTj>OPQ`u-&gb`Ipycy7C{yzv8iU12_%|k$JB3B zSY03ED&C^9rqopfFy;0R=aZbtDa*P?eq>w@Q>442P~AG-K-iTr;9*Bg!3Mi{f$}C^ zt)Ot^w9l!isMD3y;%cbyC)MzCG+yaJyMdnpcL(O`Bo4=T9#_P7MTfW5&CW{snuzRI3 zyEmKQ7FMJ$b5%;CkJpj3d+J|J+CAN)v_F&c)?N7LjzWsmNh6J3o_lP5LC&d=Dba}7 z^A&a}Q7_J8z56$?LHeF=g0f}b0%gnY0%gm-4a$~%2h=y;4N6{0sHQAprYvHNj`UYi zO%%i2P>iNEg+}Sn8p18X0=J;OEwk0udqLHjk>PZ)mU7m44u3SQ|EQt9zF%l=Lg%t4 zm?qt@b@-F@p4(i;=!Vxs8$Q+VR)1u8XMOk3^9Z#&wkiyC+0^eAdRTo41}x7k<@nf| z#4Ke5;0`VAi4p3q%JhD)*GbZuU@+|GJ;jcP-R0OQ*w?r%6PyoYy&c%(h|PuF;;t@) z-RYQ$;N9Hwz5`S1ZpL*~+EW(eb^~t3u6xqvi@kuGrGk~Ph3@Ja*!7O_FQ+=MGMP#P zC7e2xGGt=pS0+}i)2AnzFLjUn2oLGh#7>#YMo_*FCK?An!!=!o#SoqoKj!50mnXVm z5 z++6E-(p(>cnU#DF1@*W=pi<{x@EmXmI3GL$R332@crRE1{umqz{uCSrDg|CgWLOFm zvlJ+naEKz zf<{{Ur6d!zq{j^L0Nlev;gfpw;Oy@m=VMqE=4MdgzQ!9`C4Y-%F^gs~TWHqYR^i{= zlF<@1dvx1~9GLF#0*xd#v)UR-&g7+qqpgOkgtB0?eT{3$Xwy9s!AQ>AKf!2AG?GLG zunNKv9txYH3FcX?nA$aWB8%E<@<6 z*SkS5>luVHq_WS9ZCI=yn`?Exwl}Gyweql_x$@SQ>RX%MX1L6)O|x(9Rrck#2uMri zt$qN`0*y2FekpQQFJoz#weHRq?p+)HP?77iEmeK!!O+HG<*fap@yR7&KSri)>FV$6 z!TwRcEEfK3uDr>Y#cpb;zNxvowY|F9!akp2u<;&$D8v5w_@c78_wXU?6&D4xWtj_M z*&{2c(3VsA{6c$aux(bo)`@i<7F4d}+3n@Y#gr#k+Q(6zTp9k*$L-ou*@v-Ac+U`o za#noRtx@-MrtK+S64kL8uiajtj9$0;Me@c4m5)d7jKaB$A)4FQr?Nx3`rGne{cyAh zFq>{K%6o6w+$EAg^@EXD|Kz6iJsSEhsJt(ESJV2UvU%5YPfQk7_ax=V-HrXV9?*A^ z&&?_hxAj|4b!YP45wXZG$r4R0KJ~?6T-Zpin8Sjqm!gMC^X@?NUb4tR^Ii&nSmado zAxDZSAGgMp@2T8mvYf9EluVYpWU{=aa(SL)k`?lJq{afedIj-RPd9@pQkHCdvP{*d zqVK#5-|c?eL+Q1-G4A(bJ$ZlrV(mBW_j)ZxYw!|2KhGNn=qX3Xv@=SC<)n|T!v{83 zE+i4sPF=aMrD`Fvw?q^ zJw+|FPQTng|6!#$mAWxaOQ*GO0i<;fR5a6}!S$FncWBeAkiQh;Qv&^Uu=ujSU(H{q zx;mlq4Hej|OZHo?Hm8op|G}*|mg{H#=3h?gsGOE2}Z%}UX6{i5KMzv2xh}91mA#J2!04t z-j)d-gW21jf!R}j2eYTV4zstdgIU90Pb9I%gz;fYY?KT052C9NMOPn#sWB@P{2XTA_B)t;oAx#} zw|67<5zOAPHA-W$+8JiD8Uiy}T@16Q%!S!gw4bWE-4VIn3$u`Fg>WRX+4CNY+?K-Z-W4!=hl(HO?|qmx1#d->+QQr$X3yIVX8!hx+y=tzc}GT9PmS0p z*a%{xI2aEb=@?r<2B$g3CXjwW1KT?WKj7WP!5o;ik1T*$nU~d(0+nP~sQ`A5V-Lf= z=h$M{cO6>}d&sdBum>GuP3Pc;jGAZIayoV5!Mq`J|ww+}q4rzck?I5^rBB-UP^(=?>!qjDaiy8Of` z<7-(|QX8f!Hq@sxVN%V7w(z8ytK>4ic3MMS*IJbJ!n;~MIMkvPj-Z?9Y9;Z$0_794 zoj>PMnbCGc>y#Xiub>RB&|{qnngOPO=d@)_r z!P9q~f${O4mg7m1uD9zSg;9{(C31efmeg`!7qb1f%CyT!4~jt@OtoZ za3T0B@CNWV;7y?RQ@9!Y0K5g%N{F|CWhDRG!G7S^zyrWLzzXo|poWfp3mggF1*&KA zJK(k8cfoIf_kgl}KLCFM-U}`R9{^the+d2wd=P8{e+2#ud>Gss{dffI3oZgb4?YSW z20jiB1%C#f4n6@^gB11VG=fXOQsQ5^uGPcEtR60w3hv>mN?K{R=7(6ywUo-TssFWs_3i`p6lccrHnqw(#oaXYBw6;uhEENocNy2IEfa+MvZAhnlCLL%> zq*zM(C%Swl9bD)c;e(l6-t&xI+R-+1>*pEkb3|^Mv}f|09Jzfla$_NMfA8&)o61u5 zyeA{K*2ry5YZ$G4)-=9$CGV%Iz9=P0cE6!XJ9%L^a%m4%Vd6z3rEw1CMtN5$@iw z!GdY(ZqDal^2@Py)tZ`DP27a!@PP4k_|%@g?aV;`CN0oU7gR{g36b&M5h)O zUx`O_S(SK1XIBtiNL*WquZD)AYl}^4EhQD#@!rnrZVY%G9;>{yM0GUft0Vw-#5a{L zRFA6(#*Jf~Voi`|ojd;+2WrH$vhW@e8wficjV^Yhn9p$RNZf1~?eNH-K8Su0{7r*d zd0REVcU;|M9?bd*Rr9mH!aHHsSE#t^=KMVZ+kmahxEUxPIyc#<0~~u9W;K#O!K_9i zdt@~db=O-ROM~z2J9dLr^wRV8fsJ)fVJpU<+I@#|%TbP1!t9GQ6HHb;6HI|wseLBw zpYGndF#EOzF#EQfBY&ztnm?sY^Y?J%Z!yedpj_5u@OzlaKsm0-K>L`O4E_x>8K_Qa zGS~)orVF#I+F6e6%Kx(+`#k^cJGAfC&s=^X5vAM8CjR@RVb>!WKJrN5oVPFt_i(o^)4Lj(@_j`=YD-%&8AN$W`f)@-`jk zZ>RM+O9J^-+xvQ?kenB%dv@fS@TgAf1Ji5AHBO%~@#0$6qMkNu%#4dB+S0;$Yj~7| zBnakgWQT0W6@iYgW>*sqOfDAI`Ql<>*W3|E!7ad`^1VcC8>f$}ji%K(Cw+Xp ztos-I#){&yE!GPh_jEg+R+xlTyq3{XsX2S2r15yzV4l<|5thNRJz;TK7=Nsazl(@o z4n0XQCfqww?VSfZwuKp9KZ)gP- z%(p?Uo4y8A`I&rYj(UXu295>Sg08J=frG|o@}bk(G#;ddP=W0M^DVwJyXiH zbtgF(0DmWB3rT=s=*t3)?#mN$+gA;KRO9fwr+4p|UGuG9L++-_F25&}g ze~jF=Lwa`aj-IhYcI0+M8NcuP8ajTWP1ozY*9Y}l{30A4Q%<2QoMfk+*qIqF zANXl+>t>a%4Z#&A zXy~Vv~tw>&j}MMxtXo_Aa zr;^D%ZWf(1d^;>e!Z~rElC3+z;#n9tG|MjsW)sYrzA+i@*Wk0&pOBGk6Fn`5X%B1D->m zmPExYiHfCya+K2CGM@Ra&%UF)-mPND7KP)u_PMQb`=DLg<|x!a?S!&gb9#MeC`EDC zXV=R68NbC;Qf*lpG<;qimEN%k{mkyNbWd5XJJ3<4oJcuWX@gu@A{KRNsaln@g)*UQ zn!P4y+%>)xI-%17^x>QIT(;FvZ!Gg!74S9TC1!iI>OvSMx9rpUhJ7`OcFl;7wzu#X z`Nd^;ZueMPbI<);OLZUmUo#ZC_04PGnlEd07kEy$1|{RZu_P25tQIdCyD3itc>T*n zRVGlIlcFl)dhCDW*u~M+yI}i}qKjSK*^-=kB+TEhB7du4FS>izM^{zX^*c|)>|WVM zd#`NXoUQa;)m&fT-s0dqn3c)2tcBfi2}~`@#eqthzjlm&If-5+Po>*1AwEU|*_9+O zze@CK>q#y;rF(YensArTK-^I%(NCIMGu~4D#L3epxhB-eP;jGOc2S0(wYJL+cvQ3Di7Mtyyc7e~P}9(8o>e@V zMkrZ4tAX~-8Hg&~TTO>%arn4sGkNKYT%Cg^xZTMQ9nWJw^m}V0rX$>M+r|OjV~*~> zWGVxs8vY&L{csG2|4wfWtr;pku&!;PK#(z!Shn!IQvez>~pO z!4crQ;7ITz@N}>j@2>_w1C9a@0?!6Vg6Dve_pgwK889(3U}CA@Uam^lY`;J;TjN@q zXlq=HrQ9xo(%p3MOYRU$1=D$hJx^V}_Pnbix2q$!J0iC`Be%kNb2vJKq50T6EQ?#U z4pX+a8OyP*`NSewwP85q8iwqwm6y3gdLk+4@EOi$p-+@35uF74J)h&3Z6~wNxO~JC zWgS;vbypi;uQoPd%L4hqMTfFQSF#%HPhLY z+kZph+*l*6JFa&mu7;Hcmzpdg$*G(W>AR;+m@tEl!{sDdjwG-?3@XtpM;`9NZvXT> zN)XhTqrpa}^s*fJRB$)kM}T{QBf$f})4@vc3{WzZ=`@8EGldmPxvc{f4{5iqpO`HM ztXQ(efW>TTwBj}GNOkWp2Xj@ znua=OX7w#{n5^-^iZ%|#+yD`OC>V-4g{!}Jy8;uU-$ctCh4o5rQdaVD7DHkdLt?g> zoViKs%?&BXr<{c|VOjUWy@p}66ttU1lC;_SIkvbPl^)sE?RqK_ltxzbiegt(E*u?0 z@P{{d0y#r#Z^SLRx0UYEXt;ejuV{~sX^j_796VUl9c(EI7Y8{GEMf_)v5xQG|!Wr07sZBMj30h+lpl@X?^228#wUiv}@^26Iz1m>VSP zScA`EC#POWxY$)B4s@ewaKL2VwEp}}P-Lt+m`x^=eKMae4 z-!tn8KEQ>vy+orPnQFG7y|A2L-}mFGnP7LGzl~!9VTJYmNUlm+I(5kWNSF0n=I1)j zvlBh)x<`KY=bRTRSJ171zK><+?@2Y&`;D)iRNF9d%7qX%6KNdRFm?K#9VmZ%#!saD zrt6YjG2dpoD`vVYmS?Q6Hxo?xW4jiap&jsNn)?ra_2&!^W$mFPVBaL{%@8W0Q&C^_(cb~1VmMycd7PGGwQw28r*eUwPT^v*P@)^gn{n;-) z+caArV4A(zinu4Q+j5H7Ou)`ah(4}}4|OE^Eue$hkzw{{XTH#)L(HN>tgsSxs)5R< zI?}*PxPDAR47x`esFuv{b|i+&+pX-TsG#zgew^W<_s$`(vS}*7>i9iQ(k4Z|iAKys zBbIV|O_hEm~6F4Nz9}qmLn027hC<&cH-CCX(gZ(H_NjNJS8rA4st@0aV{*R!?-FT$t>td z8oJwXOBy=eBO&d{d1a?U>TGEab(It>-0Y4PyXD^HL+TXOvkR0y6}GjU@zUo*6mt~b z{{a=|AAx$73MnRZF%!C&O+7KUmEK=YM08a8e6GTYv(s|uU|v=n4B-`pBdC(Ail9)F zlM!?)*OEP3x}sg5oDx z7FD3`AE?y7K;=yixjbx>%%d(*YIy{kgP+aA1y~e$uR|LtYcZ; z{0}KXg}W+ac}$kS>~fr!OX1>Nyfk`T)`Q%;PH;|&!>*M^EmSIvTBtTV57rRSEtdx` zBw*(zGM{)Jd}|oHc?yG}(rtmPIMP2UnzS5LO%#=s0Q0mRHnp-SmxtfrSOh~6c zZehl9a~Gr&&ssW7lkp;QmQ1X4qPRDyh?!Kx3P}aK71gN2t$f`kB(A}%le+aMVbgBt zf&kk~IGbtH&ZIQ8kv8oyZ_^(4HtpOf0>mr=#LT9dn{1l7CDJ;TyGx3KhC$T$nr4** z4Dxy!Rj0K4Bk=OuOTpDt*FbaP=Stj?ZEU(n z-z$Uq{e1C50j74$%$i9T)lRpPP0XQ-<(hBt-1hpmHF4^_;UTVgKrN$k%nIbVE5Bdx z@~n;IDQ5B%O9e~uWp2wn%dNF@!5tSxvYX{t39i>UrZ}^Dd^zkZ&duGD_$Pmw;Zf|C zXNQDaLwhlTdquV z9Rcg8f$N8zD%%z)(Ihlavc80`w1g>UiX>Jz5&E`;!s$-+^^VfrRa{TjjCGIFoovj# zor=O0Ut(&tX$epEFP`ochGe>XiS(v0{nn>DDTRqk%)}*T>CW7g?#wNrgBY!fBDD@m zI>|OcX>LbSo5}TLRay5)YHAg^yHlyzEE)C7#NWfM;SsAUe<#aENm2tsU!dMANyV$d zFC!s^SCoi?3$UXd)WbS({*OKzm zJqn|2+Wno%>w?+~Cr;T|)e5%{)L_RkVmv>qG-pfs`GKzV#kbc}9N)ei)g7@JzGm)J8 z;U-B+h`V$mNqYFF*U+EVue}Q{(nwW<>1^w_Pri$T2k+BGjomq}vB_*lVU?8Q+0NgP zuEM_-RPf*N($Qy5^}u!VcAzXSgG&wzF_e z*8W1ZRY{|77lpm+rbv$GCxsWPXk2*fyzu@3Dop?M!uwt%JTViVnC&ZUZj146ZaKmu z&9{^fTs)+A*-eWJr~BP`Ku76*KG!P~+NygL+n>?dMFAdp|WKf(FCS%21 zJHkVeFvU!mVkS&;lQ7LKzc7dNmK`mun7eU*M~eAnt|wEr?g`CkR~7S!&DYDERIYF& z6|)DjQ`m|?rJ0^ymXAcT6f;?hnJmpsvNX5+vON4#BFm#bAz7AqS#ITJnewuHERv;| z$x_T@X>O9GxfPZrOJNmO%umW<9`Ok!@~yovw*h4_%e*ibN5T{{VTzeB%}v5IxBS9X zBL7qf^QccqnA>_`Zs&!W@xpv25~i35Q_O^EZW5-sA4sGR&LeI>b7(^D4HKF%rdIdwqh-sNYSi0Bw53rsR%oMr7PxFNK@i zC*n^H3tZb6bjkbL&b(_4g4~+H^^Id$g5qPPA1J z0E;%Y{#&$7h82!BwOe%*ZC7zU*&3*O6m1uBb{1{gTyf&m#u-~W>J)}#)b;gI_Zd)O z`>c<;Uq?|VW>F_*t%ByJRzY*iF<44eX%|;bmzJ;~)4;`TTC|YKe;Zm#T1G~TJ(gzm zhAq*E%ir>rOi%$U90S9-8YUWOPPOo2+=eC+j_y&JVKC?CI!QP+GsaA0iJq=C^W;e| zgLp@H=)IDb$+!ZM+EW3ewfO~=p*WOMGW!KJSKYnWC1(R&HL;OwL z93F*p3s-uN%U|zKYX4zg{)4>yj|6qk(O&*5Bl(M&{Kc%UZf=Y5Z*CBGxa^~Zd7R5R zvi3ww8Y3cPr#unz%INj4RIYEIiK1o_B}$7HWe$a-rJAcHE*SPtIr)xI2~autC7j8@ zr@BXej_2I1^JtmC1VJ-N@fUG#cod42WRlQ(lTk7Z(JL&+fCqrXL51r?A0=-`Q6gqh zB4$a#+>|8D4Wgadn%3ms5xl+J70)6LTHIC@W)IhNpI`Z5*%MVCHobpf!~SJYRDam? zK|@3TvL`A(Y+8Rt!!CAdR>QV->FkEBo~vA^w`$U@7KIG1VRm?dHpQ#CTq@80P+Y0m zIE<@ic+!079{Ew$-dQsjna2fFr`in8_oR}p5b5jB^w~lR5 zA73zHkkZy(YdWo9-RIC&7%>j!f`&Ru#E9S)&e|M>=82|i>2Il+2*zg z|K`>;Au|9h6mm%cA=hlOkn?G?KDetwzPM&mqvf<4DP`V~Y{1oflY+j$3woRv^ms35 zt$AdE7BfMMnKql7wAtJcbbJ~iMfhOgi;t=X^xiv#jBG!y#!C-UPo=BS$X*IhHihdR z`H^OJRz|F=nNc^UU3~FZad&tWQqo-M_2ZVL^e#kN3QrxVFirH*D~_ZmX3`V0@`<@E z#=p7ckzOI8Np0d^k`&q%Tu-hnqI(obP0!k=lQd1Cx_0`Mnn`1(UQk~FNww4a zlY*P%1veQ~xTbi)r6a+Knc&2%J;2heb-6IjJ{{C1eA`UwgyV^t+^=E651{nb@v^u?RMi9&VRW=R}g+=04_`}-d z7rnTrgFE9s15{WWyx7Ylv5T45#Vpe|w-xv|w~ZG2KpthI%051^KmKuxUD@SEihZUR z`z2oNv%J_Z^ZM4{r_yolMvyWTs%0M?#?6bYtFZW`Q7elJ*Il|P89 zN~K}W-W5xudt_;L<9y=dmc2?|9mrl`Nm`mFFZ;QmO3(8^h4pGL`_DzP7c<$5ne5F? zvNyME*_S=y)*mV$_aaxOI@!s+KTjwu_XD^px$D%?6}jsk$z4^bCqHhv58x#o$X#Jc z%Dvgk{TfjE*8(c6*Lu14kK`_9au+kXo15frZrO5|iVvcRmiqT+v_@2YxwZrD&Qefx zi8MW36fXRlm@!%8hEINtVtt04+B=F6wIg&CA@jMOY#Y%%ijc!Nckd)ZSR}=^+3Q+! zZHGCoN;mQBR^doS#`Qij7J|xdZv++2ulUH=JBkc3iwrUAo-()P_&2vsBBLZLGNN|# zk1aCtm47xiGB&1I-eGJYq^?BBEj~iN>LcVfA0fB<2vNDvYCB>UA!3#~%uT7o+=!4X z*DU1cnIjkcY#I$RkILToRiw}U7CadEOf!4m&>e`ys*}Tl1r2x%*Ow?=aI=206Oyg# zoU@XerWg(UWvlMIa;aX`zN{Mkh_Qv$^q4vF#&JR8gg`d8H%YG8CB6$>z4Zv(4&!Sw zEOiu?fzN^+id%6o8fI&r)x$C_xz2={{Z!IB#QD1kcCcegs{i2BSBJ+P5QS6MbQDwh z+m~PZt>(=aIg_Pk-J^9~gnRbopF3i!J479mg9j@Kw`+~w6dvIl`8}^YF*ew(>9PaG zIpV!<6yyiaQHFuHMT-7j4bHz+^ z#Y}T0bJJWg(_F=fX|9-Qu42(_zXxGS1o_4=WBXz|HKg<`A0Qy$oh{_gdI-&fl%DO2_hP z@10zgH7w!O(G|OUC;nuQgnN8ywO7(pA-HSWt8Yo#-N(H4{tO(>^=H5{!RNg89*a~> zd&Nw9#Y}r8bJJci(_Y1hX|I@RuVT}*SIo56o+s_K=b^o5WlqndDl}|7N7vMPg5|oV z9C7QdMhmrs>)z2ONZ!929&4R?DGDbzrn04}mxNZh;&T#Lr6^&Rsfy2z{TKz+WQKG`f&xuTh?-!uLxZFpsDuNcd zVivh#R-`kx75F!|oHF)vTymBBLM^eXT!nI!*JLGf25v_sdRKIh>MW`gJg!=rJ7NVM z?T}@&Ufg!!U8}DAlqa=Mcu|35FaKnL-YJ=(D6ZKY2!8Kn_L`T~$VgUVCMz-PRWY|k z+-Gh%vfBLVE?4sKq_9+ym9Tc=d~C}LD;)3s2O?vdFU+~RL|k|ZLsEEeAT+fluk^wj z6$wwwgePX>pv-L<{>?2{c$+`P7RhUjaZ*Dmf-uy0URRNMcrJ;IC zwu?I#a93xvWXVlwc>rg4gz6Z#f303q0j>@*s$i@=#F|o!*f>8)i24Z5}6INKP2Xa-JRG3jEGpQ+DJ3Jv)-J?{vD`y8{ z?fN9wETQIF0=+LO*1sc0h2dRL;aKOzS|5p3%)}~YVl_93)!e$K)O|NesasH{%~k3z z5z^xl(vK_kHieZsY8hc9Q8Z(FBlSCHd_X=0e-Ft^!PkRQ@b`U8OpRhf%wj^!dbrJP zG5*bMlN8)Uym<CSoxYvAIda z=9VksEixgoi5DbCW9^Qc0<0e7C7ow|U7m{=8H%uM_7o5IvyYQmp~8@~8r`-rL3Rff zwjN+9sNKh`{2*q66tfbVxh?bl@(gLi4*Qv%n==`43kY)8t_~U4TtOClLH6>3Eb)T8 zJQAds2~x}iX>Jmvx#ic$&7FvN70*wW#dgC@3MqTp1%>SD9K?>qn3J*7pu$EHY*VkSp1lcTvwj^>tMj+;9PaSO4dm^QKvhuR15~a1^IpJr zL;@Bw0gIV{%}oL}w~ht8t+fMmZ(7+4lbKa-vH|J&1bQ?N-&ld_9(~LZ&ZUI~>h`1S z+6eTH1*-QYGw;28r%;5~2ok>+@?P`Z9q!1=J3lanpqC>h-u4Zo#>YtCD>v|D-w6x~)ljuX~hw)SdHu z;nXv(ZsMfzl$pCSfg?&j;iB;xgzW5DeBap7ywDGNP!&$YDb&0^(KWBPkCzyZxA0I{ zmGTfuaD;ap=oRB&P>(wl)IEoRmw|_a`nJKK>UZ~Yv(@j!Oku=Q!8879+U=8OZfhbp zz1Q3}L5W^Re(~Yq2NW}Fi;A0EGUJl1f@_9 zMhYcn3MFRS?U~yzy}y)GUvpa*xov_%^-+HM|A|$42tgz#NX8Ya1FPg#q?j@Bf=O&= z^l|G{(m1(-R%g~}I7%X=IsueYodoKhlR+udDWH_;R8Y$F<4BprOqs;2Bih_ncz>xt zDr9c!BR7;Odv=vZ<7VY-2e_QA>C!$yLmxk1keyp&S>M)eOr(u+!t4R-)}3IPkqSDN zL7fOQw=-ddGpHJ_s@SJfN3x4yCT>Yp&^>Ce8Ogb7W`FtHI;IQJ}(iCa7rqX%vlO7L8&yANIUl?N91H3@Yrs+A$Qqv{Q5?HUT_HT}oKDyC0OK=qLHI1l(nMRxgE36UY zxvJO?Q)$u?e2HsGjnF+xeP?iXUKGgE%yGrHR$33Q$?GzQqe)Y(jrYYT8p$Xu)>ixk*gWbP?(A| zwG8UFlJOTgk4Q9k=pOkwo3o=xtC`L&&n^$w8}b|lqbj$TSnpJ1_2BI9A2o@{P&g)o z3d%@x%;S%xCpVh^%pMl;AtY_{z4;i|uD<*%mJe$AEZ z+!_-KB>N(dj3vINrKMTp`1iq!MUr&UB2EG?99>F7(!Y3gg(J}vU2`~-ty8*3epKS^ zAiAbcoobP#=jS={MOHXy{vitI(`qiX4Q$mAT@ibL-k(foUnHs&rs?27umP+9F9sFP znV`b)t0*ePEGorP!D4?k?KXcfw=P9xFGYROc8a%^Yk@zg6gWCNIk!p1&0}f;FuyR*~F;q zAueR4xp*Z0g6qk(sdSJ0NSGZoF}gZ^Y@$T`HQX5<`QkEOkv|2Gnn%o$ok_+$?aNi! zMh@x0I%@kX(F4~gr^E`x?_m>LZ@GKqtA#yA2W=}~6<(_p=oGZav zuP2H7If=_!c}#4pY8$evj704WnLr6%VaT}RXW?Xi6#i7WiYIdwHuPJ`{L7q44beUF za{%Y^UHIpYoWZ3@4bf}z9Qiavu`KUV+mCC~N%lJ_97#*vOzbFZ*MJINi;u0Bk%hI~ zh*@lj*+e*VTaJHobIH4l*=XO*%|^HEI*pg|5y{0Yl%<8)=yQc5%&qjZ(F^@W4R-C# zZUcB0+6f%PR6Zpz9k)% z9OLM3MgXnT;YBOT1OX=zBKDxdEDm{JEN7tGty2LEH#4NhZP0?j; zF1m7O`-G(g3Y_#7bZ^*(|2;3?lmERg@80$~O%-Nly&qN0BB)JIm4ppFZg&?^qi}s2JQTbKtOoA|71sMf6$synqE5`BPRvGRnAFDh+?z$3n4-z$sx`#kT-6Bv? z_ZXdM_L{N~EF)WV6ZW=+E15GK4P65Y=2KYD3i zLwm#6k**{XMq3fPerk=2R-Uf{ePl;ls!B(OOMtgjQw?0#+?}MGGlQQE#A9{+l3*c~ z33sTT6xzocSEsW?tm>4pK``sq8xHHyOG#XEIhe+eOfU{+g;Nbh*&BIe0>%EnVZ}i+ z%x0C|3^P;k9oWCz)dyi498(DX!P%cPJRX6RbC!4q)k~?+Mb`T{c(MUc_sEa%*M-eP z&9rF@qE3`z^{zYzRgaFR>L;$MTCxG5MtUg+=}%NoQ5{xd->)z2v94a4)5NRr%fuj@ zsEKzmdZ=%E3LFY90hOkfg6heC7Q7sM4%GWTaQ0|xdw8@ zN;-HHH!;&|#bY{eDi>F#xK z^pZ0C9PFATltCZj*iJCp99C8C!uf_;Xmm(vbVxDK#Yiz!p+7Mw!?pd;J@TU}?|S5J zN4K#xQ>IRtHJP=``)T;VjA=FFYWEyG_(GONst+jNN;u)6_bWmb=KZ|m^WB0u3KK$`(+^w@Dtx~L6@f}>7J*_GfnwI$WNu5nzs_29 z!|A)&92U*7_JOALy&A~jBb&4ORvwlxJFPfjaN3)JLt|roJy|B}JPlF_ue01Wx|w zQOal0GFcW4&2vY#LE9BKGh?dg8yf;MgQkWh$woVd;?Sy z+)7aCuY_2$^e1NNPb?LDkE@E)ba1a{DVOCa#?!%1aTBuvKhhB!@FSKA`tpVLyndc- zMA{|2MQOLOtfi{lrO&cQ7Bg=@D=EK57`WWh+xl)>hL<+YqAr$py|$%lWL|A4v~vBB zVSTgjz0=*R>6B6Gk4U(~NQn08EHg0P$|P&uJuTJcY(V1Q&O%-GtEGBv^T=|#{@m^g z`q8(gvYd?;s@Jv*Eo~l#fmrMOHV@^i_=*;aP;!-zu9$<;_5#k@tOCxid|N(ypkh(W~gcEoyda3suZ#0fC75of`+ zbyvs3dOMcSM$~gv$yPe4BWWZ4hFfy`4&9?PBm7O_g3!b%Gis;D8i$y^$9}1{!tW<) zDar|}_IlF*F1lcCw4O}IxW?(uawr-T1*SW>Cp`3>vKz|uZ}N_RARS8vtH4p-v{>uKLif~ zH-L(>a$?r(h?vTQ0D{ySA8F3Y_JNfSL$Y)1BA|1<) zP$+SBqz5QF(i4;&=>^J;Yz4}Wq(Iq`Qc!ke8&IXiGVoon98@{5Hz+%@9Vk1J0cA(F z2W3Zg1Z7A1fU+Zfh*`5EVrECgQo(&(RlKKz2R%y#kHMrB>EKDvQo+w*(xY^s@QIln zvFFK-*z-DSyeM_~dY*ir;XZavb%6Fx$e5!q5p=zkf&jTAUZ$U3JEL(@gKMdZlGS; z56PN3iL#iOl(C1&jt_B^#u*z^9sP-oQ{3aj(J zTrI55`?#x8=e-@<6m?cjDLHbsi|QQKL_afJ`Be1iNLp!C_>&DR zx<}D2{C#07?JG~x>M`x*olnkIt4O4`MfIiv#ydyV^<8T@iWKzfCIQuQN1srr%NSnz02s$Ky~)dwMQv+81|>SC$j9#Z1-hc~W(IUI$j4ruEQRN4I=^VH^7eJj^f}Jm}LUWODz+45H`$ReH=E_v3x1x zX|76FOE`5TGl3rWNu{|F)Pz3MyyOD zW@Q@1p_OUGQb7?qZ_iVQqCHRAKf9z`LyG_18??W}Ug&5y4RJ2D{T}+b>*dm1=UU=+ zf$08Vx6w=TMf;HiUq?LJ(YdIjPzB;s69!TD$j=bY50I=Kg*pn=NjQ_OcU>!L`*5J} zwro^yp|WTNN3l4~W+a4_Fge*H^3}K$u4GjHCT$&vFM-=}{kP!m;P1f0!IwdO$19+s zQpwSxQp}=KEET-$ucm|7JnO76v9LW-^I9uUp)0Xu^#j6OljN?)$1#K!C{MTI6xT&a zcSQ4uBI__1$rR_XS5e)EYg$HTnnz^lK6G7)8G04fnMe;}#(Eu_NBVh)>U4DKlQeuC z7I!=EXq*!4!H1gR+ZSerPjP03?+jSG;Y*=lal@yQ?pSB;=0ta_giY!$!zWppG5i;7 z1o6dSb`p&l!);(@4ApdWs=K-u>=eiHrHTW%s--HHQZsX(iL z+w-I+_Pk9hZe%6z*Lf2ibxnhVhBDvszM*Ak(U9)hJ&KRW8zYsn1~C;y<(O#DK)V%K zHt+fD@lY4LBu%-d`NSgMjhJbg#k|6>Q!%5G%g8=ar=n8257N4pp_$MY&;i*zl!Z=Q zXMilkW)*|-SAIJy2Gt-AvkRBN%r0CBGrO<=W(A^~VP+S;1G78S7%$&7bRZqnkt`(r9=8(`xuxzQ!3Dxqg$qgX z3^LxusMqB=J|63!mpw%o-5{L;g{Qb}&^N8+9^Dt(hKZz6eQ!N@9C#5Z+b|haQFRJ< zKR69s1WpH^1!sUSfeqmAz((**a3=US@DlI?a2D8;_kIafik%Ja4qgr_UaE;bvkhWq z8^lt<_qnRLO}kD-F&ho6m`?|?Lt?4mIhb@M?K&03Y)FDVPxWtm-hZ`ia1)L(R7&K- zNbHO+%0wN77z}I!r3E+O0Nc=~+4=DU4k$8&wt?d2W>k^>Gqzz$9@{XMST1ZE#<;7p z4d*+yDYoHNv?|%Ouqn3T<8MaGsYc16Gt=N&&^ogXSCU3$8?FLn8=64bhIycD!_}Z{ z!+cP-;TmuW*aE%)UJL#PTmZfaUJuGPECgj6ZUAK)z5>cN+ziS#+ycrrj3)NXHi(&R z5VK|q#jQ0{h*>j*V&0l5#H^V@x?;@~VrCocd9n@myu!AjzWaj8bx}iw4Fp8gE@8F zhFa;{ZU^@Q?*Mh}8{pC4H$kagEfO`g6En3Fv-SbW+u8@jtbIVSVeJEA);^&4v-SZo z>&CO^sla2;`wy#~%FIc%n}%CqwVUd$O6{gNwkc}&Ja;E+V_j6c+}fD*JI}Gj%3Gc3 z9q(2HZAB~$`Bd*NR77@39^qSstzK54|C7tu zBOT1i*b5lL9F^2}C9UDTsGPp}K5#$q0dNrbAb1S;5GWZoB56}eF;ht~%Y+pVmI;el zCahSpOjyh^Va2Ou!eZ7}V9!%ufj#e2ppw-DE~%1pa4W2mSGcQE$;%zv6qQ`b-N}0B zrl{m6(2o@sHaA))lU6dHNZ9QOs1*o+p*G=XIcx(fEQ`@h~)!P3u2u$T$ls%ZZ0pLd~QT z8}Gdt-BGcjV(m?{6$C#M4CD>xx+YiI(Vw7)nV<$XpR2{e1ejUW=`gdXb6{U~SFeR# z?N~nb{VG>wQA;>=BrWPP+>*n>bdRD^_)=ku8kQqJsbOJiEy*4h)=4>1;ZIu5pP`2O zwkN^@H7B!!$ zK8q?!Ri6W;s?UQ`)n9;8)#ad6bpcW}RTVQ;6|*XjWNuX+F{|<@My$#sW>p@=p;dXr ztlVkOQ@PWg*MX{1?ra~*6)S#bTUCwmvRQfIcmO8ab#(x4{uV56Rd+)WW>qDs(R@NC zI1FZ1wF+idwH9_OSBrysm|4|}VP;k5!oKRRE`;6USUz?A8du9XC4L=AtNI3R$zfc& zN6{&KWf%UrBd2yAmQz33VO;IDb(71f`mVK{@ml+@QBi&K3Q&36i{KgHOW+vrx1eNr zGmOE!E<%!Y3%UTyf6m=y}_c`6jx^Zsj8xSCHWtipG4 zwXh0*(_NJcf5Wj&QQ?)`ovdnYf(m~E#oBPh!p7@x(hBEO;g?ZSsqibHRQUIxRQPpJ zD*S(-RQT&i+EiG~R9MWaT8am&YKd7@OR;2CEitQVDPFCrC1xsY&yxz<^PCFjSwV#* zQsV2aMeUx+rBQY#v|5roneAACICjXW$K#Vj7hY(;8wTY-ObD;O6WScbZRLCYm9f7#gUpv9|~aF6o3 z?#y;|yC{@4UFxR5@^g4;bCssRYEccY4H@3ILANXnZ*eotw%0r{%{^ygD)UfJEY)E@82-?fi8*rCWyfwd?MM;e6Na?U#GXiKQ(g%IT7Bqf=TBbZ*F z(1*N-GjB^C?3Pp}4IQv9<5t)Yoie=#E38c2P~?u3>2~f;DwFO}WJ%fnR5 ztb*o-Fgsn-E)AOZ=_Z1@r4(Gz?GikNhr%Z1k`b8X9slH&>(Af;xV;T3iLC)u7Wpe! z1Fi+H1m6L_3BC(H4Xy)U1pf)D(`7xVZ+{=udz5HRNySV_#Zqp-l)0_+{@l>1aEWvm z+gkOddPCD&glC)OKdBuwG2c1iU&(RkIO@(uS{@c z#7=`f;@r-I71oIHT$L%2=yk}vq`}aA_AhV%_-~);9**Kc z%;G`J7UVRy<@h%@vW76#DZy2`9D#L5lp~PBloe>rgAqkTdbeA98Dlq&D9c#)X&I4W zi~!bNL#f@;3EM>v=-qN+xm`C?J~Gp!tq!)gU+y&wMs&jpu=bXRD33U;j~kK%4eMfB z)`?Js4PMUL$I>`kE8oZ0nYDim_Jq?7&EhPawqN3^(pi|+l(x6=zh%+paVB*`_sGvu z&el$?eYU*{ZOF^Y8OVeVeOJb%=<|bV-H&@KaySS^1<)lGRSnklEGvduR zd@7!`7>@1Cv#TqQFp8B?sv6h7K2!)d{Jwg9+bep@@bc!WwN10v1`WGvVT@sH$-)3v zjiOr{l@GLJPUTwsS8Zr|bLGhTe!PfTT{bVKX=!Za>XJkFILk0jj@TDrGG`e#=hD>c zDcmd*`30=7qWy-e8P3o^B&*~jyXCgi>mKDD&v3rqdVos-tJXq@^w)@KhI zj@KA=75l2oqoiw7=-HbKk9t}r@wA)`-nMP~!Rqm7Q7quuOZb<&<;`rI=tYFsHdFa# z#2$^#jLbUhf@1z!6Hs&E88OdM}FSsTvIq(nJ|&H z_*}9Wb3wAis8{AWVmVB-P_YuEyhjz&3=Rse98?Y+<9CQl_m&-rTy3$*c!HERsPe#} zY^#0axVWbpuFi6PoTtzf*n)oT$yKRUDJ(oB6G=M7sY1Gra4nKfi6Y;Ib!EUnxaFcP9ZC}D(QzRn@l09wkA`E?orGy=6w5OPa#SW zVG7Ak5TuZf5`-eM(*!XNPp5HmDAg275H9~Z%lx_jlEGTe63&yI3u{K8$&yPyP%qvM z)U$U7CxQLJJHb7`hrm6-#o%6`zHnKT0>mr@h^2ye{nd1!MiX=EL(G^z&5ba(V zpEoyBK>bH9F^~TiHwPn{)=WEd*_V{nhXLHq> z=PKulEvQ=7DtxYT1J5P%S;Lfus>c^pJ?@-oP_*j{s;^}fkvX=QllsZeUQjt7Ki)lY z=S}Vr&sJ{eUiOF^gk%m2DwlDK=T$ecUaUP2kKID|w(A#EFN^KbHn(XRJL=BGOB7;? zq&dsVl6j49-df*t_88V<8u&{Wf~KXHwKr5eUE*2+ z^+#PBx!o4saTlzwyW>Z&D#xCL4R=h1-^q^sFYHjq{s`OKF`035>qRua?82vFjP-A+ z={Q43Gj2kLnG6+U{@3}FLjBG$nc3euHWv1hV-m_gIQw&kMT0HppHw@&|Ird+#U+y`jZrDOVp`*c6Dul44y!zR=%}On zUr;k+;Nz-`G_}WPmCr{M6 zVe%$p(MVcz0oTwySqzOo@x~!u7CV!>oprTpne2&fb+#ldiv1lwb2!68@=~@V>m`e! zV?VNwW5NBw)4?x*=Yj`<%60~Vde0%C+8ho8e*g{we*zv3E&&IFDq;@-HAU`7P}%=c zpt7f5lG1y*)=62mbWnktSSt7->;>m0n&r-o4te?Iz9US3VDi|BNjgQ>Oi&pcYWHL87Y^j{vw6a^-TuG^^cJ99^zd83M zEobyES-=vYC(a*exmU}H8$4U22RL6v5VOj<-&p^MsUTrixguh!q`yWuZ4mc%gew!E zJs;s59SGPRyY>8v%zME@{uBbmTCpnzDu#-AqQzfv&=2;yb9061K`J~fdsHoNVd23V zE>$F8wd4(&SE=dBK|_k#X4w-Z^PX>H`cgE0WA&rFlh&THCwiAXQCU7@MdQv3s#dfw z2=7~%nii9nGn$T0FVb5_-mZa8}s#YGxE}rb# z0V^qcqPl#@&sjINveiAMwe5B0TMwyf%?`(bE*!1RRjXR6S7kkam3#gw_k5n$^!@>j z``cTGRJS(nR`!S+$Fho=aY=mC^lH^>R=>(W!sYKf)`fjox2E+6G!7yZO;2@KK=!Aw zQ%?PPmAcMGE~sAK{v59<%tK$O$F;b}Q4yzMZKdiQD_x`7%BI;XgN6w$RV&@jrsuX) zx9(fLh9|D!i7Qn(_obRrtFjL+dqnFfRj*>O;>+?9m8g6{Z9&z`b_WGEHsT|?UUsYU zv)g4$tU{7gni|uzbWp6|R2sB=M1pdMO4HIU9WTfDjK&LQ)YeQNR~Jl}+Bjvr8^fsG z=Q6=gM9Ev|8si#aRvxO0+c_q5~Cd|rBGywU^PYU0Ea=KdvvvN*f zn3Zn!gZLRv z=62z}R_;?%guIQJQG1~aGMf|7S7TXa@}j%2%HL&s65Cy_l~H=4OwzO=o<~agXCYG# zRnJ!9QO%L{LEO_|uKvtCDag_^J*{2K!rWA7O_3|X@Nn_MkDWMQ@{YsE^AuIbfV+d0 z;7Q=|;Mw4bU_E#eI0HNdoCS^mZv;;RZw1c)9|K2$zX8tzUjfenmA9ViaJMzXFa2^{ErU*TFjQJ+K~pADjfzyctXZrK3tq)}$$BRZ6jxTLWAg zpLV&om~HPT-A@NkaIcu{)~+;?4t|N7m@T}aRAmcqh^2!0yxrb$t!Jzh9JxIdxvh-c z=-+gA_`b+9Mk!;t@6qjfO;*NI@?1p`QA!JJs5@rr1T&}9m%dj|-VbA*I;>?n>V--w6aBHXp8k$&fjrR*Enh%lvRAnr}pOAk=w$^-`!C5mTyG4? zHrYfC#SvabA+goz#1C^4pp(>>?3u?D`Wm(@g zL?+hx82Yi?7_a5jJ#)Q%m1*p1C^(A@Ow|CTpLg&FdgXIMWup^Nu{M_qNh^94UwC4fTizq zk=vb-o4(w_N-vndMUmT*$c@Ha_QaF6;OUNSbOv*-GK|p`m0w6VlBOnLScY4mM}<%DZaz4)N~yB5S4P zK2Yho-^>1=@Y`fB%49Ff)|8pskMM79Ie3I9;gz3jvOeQXQ`||WebqDVYoOBfpl8|#5z|B&(?nT8 zFt< zD+%jf#K43?wN(tns8BOk`$?`H*Y13TV{T>mLThd`R~N2_wUK*l;JPJ4Npj2xDWjE3 zNphDGQOsjq6I-TCsco#CTs^zd^$Aj=2mOm&^$JxCRTdlqWg`L;L^-z%)K`#UnVTfB zvva!-w?Y^8Yfx4-%h<5qpckO53SSAejq}$5Rp2OPhh#M@4(T(fecg4^PFj7^-5>u0 zT+fSlj>r4s>1=P#(IJ1{s8Q#ro8CT`e)e&r$DDHR;c}|2pVTl}UG#^JJcVxei{o!j zcDrxw+lMG8%aj;~P(~!wkiPq5ZsE3r_~Wh~YG{@c;SPTglfnyb`YTsw^KwKlZIUnw zdjBakmerjcnhf75r4F@xBPUf&YDY6bL63lIDDx|jrkygQW@-~^!X^dur;-l2?Ys^2 z-X_=1Vk~M`Sqmiee!L~~r!=TE!AS1#5}(#_Z^M1Kcl;(LUp#yW91ngQyaaq0)Hv%S zpwdfr@_oe0^=bEBQ7N|*YCq?8C*DNaHZ2N~c3UWmN(Gfj(86jemW7r1t9+dfuJV*s zhsyQoK-HnBRG`XVB_6J7m=XOKS37dZywkmJ}jBlJBUVbq$Tm`N2@MT-AnU*B3zOicRP# zpmnw2x+3{t8fkpqHN%+);CF=6FzP)v&5=t>oWWDMlLmJs>#b$D)+S1r!l>So*?EKb z}=PRRxisJZG(0VMtPXLv}rLWCk5M_0rC|k8_ZW>cHw}cSGbI{_ld1eRR zTIVFAIP*(p2v6heNJ{35JfD(~jKYK_*v%hKGS_?rqKUXYjP!(;(k5=l&9u>QS?N6y zlwuv@xvrvTTo+|r7iB9M%xx9^&8>&Uky$&?A8NaDLB*;C6|3otVO}b;9quI%#zuV7 z!#cW4=pR|tp+})%Q8^H!q70+LEnua?eieO7>q0Nis_-FtGbu7lhGZEL?WY8xYEajv z)1X8eGamNfBMV*OHIqR~_$V}UnOm5uQd;IFbe-eeWJU~jA?8Dw$?#<;%Ty0TmAj`u zg4*3t%1}u|K}B4`2@M5hv=hjBGG&hqADr_7ol8P|Evse0z$HZzz=v zI=($c$2XM95*^>3VlbGR!QxPlmnh<>T$N=>oVq&a2(J}TOm1&H>OMxfq+91*;PhJ^Zl?2Q zLl{f1gF3{8xB<#^-rZ29^B#g4=ANp@_FzXT4<>cqGq|ZZmU8P#>b#Y>O^a)u52^F2 z&rM8Nsy^5BUANX#YpEd~)mA9E&5I7LCcR!rK2Pd2@kM1+xXu5XPCJ)Qi!ISj1$-WJC& zPi>X{I)c}4jsDVyoBle|QRuJC+Ckn@>$cIDV9gq&1r-~ZXVT6DRhl^+ur1Kom0OzB zQj~diLpnQjs~9tPXm7jo&)|Iyb@dvUIYFb{3`f6@I~xAWPO!lUQK{f5-l9B_4xaW@%8lJCC!_<# zCCWC}w>SnNFN-60dzBKDdAgv3kUe@9T?V2o+=lGSF#;Dj|bxt z?O26TyKo5i3sH7SupMB2Czt@I3E!oe9o}wYefgi8H*E z4&?;2XH+`=hU5mz-Aix`+%*Y`G6{;ZHJj%40{+b{!EwLM!UCqwZOANg8!L3+w+G)B z7r2sU1I5Geke}bfc`AN~JWulbb39A(TVcd+N%Cj^4Sq`i@ThyQdlP_4;!y7JQo54- ze+RzrgZp~V|5JF6@n4kjUzE*TF}IcYH@6=A@2+07YBhh(UtHt{wrm#KUcApJ<&gZc zIF_f91Ick$QWg*5nv{jYNEQchzxdyfg~q0pK+Uvjteq0I7xmj8|0C%nWutT^W%DjF z*%$XNFPl&DUXzU|lZ`0bqrlu$f0>)J2XgIEut76NN=FQ6(L5A4Ri|LY_<|Oy<1SPg zgPsszu#mM{f4qpYBAzEr{!?+&XU59~2BrJ08t!K9A2&Z z0#bB2u9yOPnU^{R6bj)b92)DI&^YRT5zkfH!%OKA&--x?@s1zBYo&1msC53z^ZqPi zH{Odf-it~F%kgDyFMFz&{ULhcJ%Nq)62kus?+5=Ec~4n!yzc{w_kBUt>gPqg7iGK` zWxO{x@!s5$I!Sv%wCqw>IKa=Qs>5f{p+DMhG||`pjI!mWR8WP1YNbX;?G0s9e~yI8 zub+QW)tc<;&k}L7CWZE9&HU(@gQJ3@F{wk-9JoTa_kQ)_Ne= zA5{8^ygV*~_f{4}nLI?1pnriM;^tSFOQ~39$L{*7<^RnkVTo)QE_>^vIX)u z@c0iW5b2~%$s_IM0n3~`w)67X-pgZRBo9$04^bu$bCW#GEss2Q$Sn`sE{iQx1}J|uR|NTF_a7GeWcqx7r7P`}- z!QBB~IPlL2XGc(mRvD;t?gUDSO^t*k%7i4!gk)|KlDWCcJLhI3;c^i+wPCLi&FBpq z^jN<*xcx-NF(RK^7MUu>lnIdXpG;xiwS=`=pe)6E+%nRv2i6&$|)G*b9kl%+bz)>2OjA3Mm##+5MD9EGtu%*Ds{!5tYmjb zs7g~gcpf+yRQmSt!fr-5CTvkAY*D5!%uV{j+%_dJ`sV)g`a(%DeQ`BZetmJ3dn$c# zrKA3@^~EN|+`EEEJpIq>i@ki-*&CF;*awup7z#>X>cKw0m!7XT zbJZZxW`$8-mvw*jbl*%1;&thNO{j5APAP(++~IX7gcJdm67RF?0Hie-@<1=O>)^6U zO_WJZl(lxu?PdI%TY~ek99*G{Vp-HvVf^FET;nYY~Sa{ouw_w`-gn3U78xJgd>_CNl2 z%bxE43lOW#vNX{`ofNe-|7oH22{N=-1Gm|i2tID|DsZE z1*o~Lz`wbD82&3&t{;*bX6yRGK63sK-rNqct>^!7p8v;t{-5Cae|yA#QO18!#(#4Y z|IIB~L-_u%6LYusb2ag2M@{_86HWZB>kqr0G$yyliub>)-O{qLxt(I!m9(oXjwUVP zr8Gzfsa}-s)HNF3OP9hn*UTRSs{B-VncNx4M3l)yRLbqJYHrWs-`w)Z|nV?6~ll6b>QX;7hw z_lO@5|p{Q3g!R0Z%j$Cw(dhv`P;hR zbWblQ@!xQibV||&zYaI`G2p>n$u5oG;F>Ix3Zs^j@U0Ii^8Ec8i6(9DKf?xZls|gy zAp1LsjdhTzoR8!VFMUrk8_^sVQBYVV#4s=a$2D7)lSpt8}|VW72~L|HZxl?uMi zQ}I6?$W9hzGd(07o9Q9Sj-QY`({5Bxl+E<8_i3hwy>C-l@FVRX`-N*Lj3VK0d<6Yt zx%5Z8e{75O$0mDEdgPC&X&hJZx%$X1M6eS4Mc{eh#i07gs=dG;K|m&OQ6_LvrZ>#( zS^S&ZriAui=_6B8Om931m0xc>;hsuwJnpFf3B9pNQTMJW5>I>Ni;r4wO!V1j5-7bf z8I<0r1*JEpfYKX}!C}irqAVMUGQA=Go8Ay*dPCANy&=l15<}Bq~9epT|op1T$O7_|0D&h(w6X784)95R)D>es=&XcCv zYCGx8dTZh?o7wv{mN#!%@7FXho9SK_Kj!027NSfRqO3@p+p{Fz+vMXcD3U>IO$$26dyO=0MGN)Lf_q zj%tHi=%~*?wK+2cTYYDSZ&?H;#G;>er5X80uw5JqqA9WMS z@7vsSQFqCq+*P>CkMN95mmtm(n$$RLR`axmdRKnp(eZ}xLSjAY6eh3O#hm6tj$~Xm z%i)Nm!{JGX!;%h~2%YtgLy``|P!4e)$0U9qo%nrJ;`fn>-$x{VZ~j#69$G~CCcIWb zNQ>;qt=y)o5v?J)G=UoOZvoY*GaEb(yc`?@UICs4UJ0HFUJZTAUJFhITfutp zIQz^HQUu8BOT#K71TiK%0Yb#qsrGhW=ROL7weA!b}qfo}vXFb(p zC?g!ksMx^faj-%=`gb@?_73iVtBu_afm%;gwnIn$;rnCo=iaIJaV2e+t8hChF^Z=! zs_LZ7Pow#0SIl;q+%Ty{yC|C768_rEn)g)Rm~FER3!?xPw+cTM^Jmg9CaI263a zXWG~Je#^9?EYpgzon6dr5&q3BVY_%1XYD-TMoH7#imUqU{9G0Hd*iB#VDhY7g%MYG z=03A`uDVt1Tf^3*L8G{nT>T7ORC+!ODowX}uD%g*Rg`g6lyTME7UAFAa&tAz!1=h# zD3#B>N?CqBAHdVz_}t9%6B4;sVf0;};NAqEvA%0JcSoYEY%BgA%^hBfQ~Xt0MeDl5 z^Y`xRrAYt9mbq-8n>&Oh&SAORvcHaPWL?fk><($y@$Wbjq$GGyUBIsxP1xK3-1BX2JZ!@ zfcJsL;9AmRI#rbAN>Qoc%RH4F(!u?nvi7>9WbJiP)?Sxft-UTP&YG(Y&yMcG|LOr z)@Q5jf#aqh;GY)pV!wBW+o*Z~dvsGOR=LN-tD(`AdQn@|$^})c{mxu%73&ewBQf*k~ib=VnZpEpOtES$C%C9~*@^mP7c&TuVrl=?$6^_ffkLot1vktYG#I6_3 zwQd)!uqN_HTZ|f2ao&07GbuQ-!K@(AwEYsE}sWOs%DIw(YL^@e(;y&*UuU#w)VQ zBrkF)1iH(h8`L~ckNlSGQNTIVWKiobN`Z!yM zy}?o07RdBI+7KtK4Owla+AZ^Rxl6J3q8dGvG~`;LC{pK%TV|l8~ z7G}7lZT(H2C3l}v7+EJrbN@wVkh^-@eJU({Iz*;rQo`o;j=h-P@!hA^5TP@@mG6s) zIm-9Z5P{>4PnFWDEZmnntR@%t30jZf_cNd*EHz~Hg(#~pM5ThY{%P91!`!lE#q4AR zBn8S*vhFZW?QQKQ&*feOrv^!H3|AHPsf0PGFk<*#+&|nbhDT@d_(^m+9a#%?HR5_iXBWkzGNC}XmyRIrNB=C;~Xelc9>Ieuq?by-#- z4a&<4g62Kjw2VRLzir%!aPHh|ZzaYXp`7YlGWRx~OG14)rT6t!Y+}&7109r^D~p1b zZ8=`fy>FMgrBp3-dX@XrAxg`=w8KHM-{GM7hLOi!|bLpirDI+xy_ow9F>AXetru_9d zGgVtQYgvJ=ui_z6CHFWd`y+mko0&ce|I8`)0kxVU`4Kn-{4uCLu%Cb@gU^8as-J=r zK(bh?68;>hg#8S>AN)D^9q<>RYR~21(CVNp8g}Sb% z=!#>a+n3i4b)r)zSblwUB2V>6PDO%A(Wz`CMW-Xb6K=hS7yUgj+Q{E|C|6cusDC5OPBG}Rt^ggYrYg^{0= zxX;~8$tjK;SFUO#oCBaeW+pYTS9g7LlRE<-SFzJEy;=MBnm~IyPPU7C+HsyAr zWrbW1>6TjU4I|5fa^7`|vmN$@Dkn{4!C0slDTHOgc~JQ!K9Q%qBumZ4CH@T0PE6Pi z3L`&fa!=T_#H*)Fn_)*vD54x!F0rfTS1y#;-SJ7At!Wif`QVp?bTLP&cVsMXeS>nq zHr$7M_b#6U-UD~X?E_F{`d^^F_1~bW0iU`%E6ZqB<+4As>s4g~}TS0otIk`}Mkt3>#n`xNQv|hJh zY=KUBs#wn1c$F`^rD-b`jK-noTJ>t0Q-{Z@w?V~?F_ zy$oe~mez4_IJuxKI273*3{~i?Z5u+Uz|H*0#y-eBod9*9qh>%I;Hb-?_IH%N=O5fd zxWnsao=R_ph3*eKiS9lH=supGn$X({Bmcs)!+`nCW52C<^|YC@Y|>JwCzXbnDo1@M z#t^pVXODJdXaK0kGco|=3w_}1?xty1);f!z%sa#laqPVy%Oco`koxvv+|!z>XSOug zOr(0!;6=H2ZkSz9JJ5P!XA!I4+D<$FhX#f@b#H(!@5*{=2l#BJH!25?rwOUtr)KE0 zJk}L8L!D*P>fG#)WTSE{_0a6yQ)n9{ISncgpcY*Fa{zcTzstZ{a3^pExC{6YxGT5< z{5beeupHa~?g{QhJbQr$fO~@~C;O4xtX>mk+Dud`_y$jvKhtjFw5U|@ByP&7X}3k5 zDBF@vc{Uxqz*A8vx3!^ic{=zVZlY4I?_A{~9sC71QK{g0QeyGG=&4k&I&$lX+|Z)e zt({A-bK9S~8vZ^x7}{&ndm-InC-m2q`HcpuiWiI@Duvo2WrxufwRYI-r)Z7NxX8@u z3OL`C?Ju7$Ur;$$;~|QGUZuI54X#EP-a4Y9^s-&*J`0!Xfayyt~q)#LEQBq={VifH$f|x>7qQe%xKp7T#gZN%NA@bX$;RyuQoxA;)}~%Z_Kfd zQjR;xQEx>4{sr|AI+>wH1UZ7Ig@KM^H{Cr7>X{ks6|jmII4iylA8D>o+`IU`{+7$XDx*8KE$+!=gGwy3bQwUg|+AhyL#(n zH&zWF?hq}$L#fN5KBUrR*B<^5Zyn8}L%g>}6^H#DF&!wUQ15y@CsnL(cl+C^7g>I! zcBKnZ#WR~OaZXa5m+`F}fIv5lb5fS@>QUK2Bh7c=B)wPL*hq)AUZp%Vjc`7*$kY8e z&6213$&>2%$U%SOwk%sz(1Zm;vtrRR&}on5GqFnpTwc z=qcCPp`oJe&`{++J2X_3jh!i<+Sr*W>-kg8wwY_9Qo#enYH=uMTO5rH`ZOV)8PqTb zF(2L9`Vsf3UULyu>jm{#PLTVNM1C_+)8P?n9NH#+DA?7&IGE3URI z4F^v7xqfNIJJf(wQytU`OhBMAaOZ-T%zeJU#KJGm@;;WMN-2Ljs#K?lt9mGTjQ^k3 zd#`(=mA@r(PlZ*}`yxpyxsEUn8Z8}}IUVf9XX9%a{ zDw(?-Zcp^#ZSxkD%>4jvC!#1yMp5!u-;&4XEB@3h(frDHc-PXtl7xlaWH?S*hYRj2GOrgkpF#NU?~)6ai5DZOZ~~^y788$7oQ;Vm zV!X+MgW})GynkHDyq92*f=BrPp6B?&%=>#Zmsm8J_x357C-IViP@48qX0+f%nP9zy z=z7IAV(u&{@`nk-vdkRD`gWC~fWPBWAL3UQ+hh4>FNu#McOwa9d6mr*if;2Bta)6? zb|TBXe{jnbQX3aX=KbSaKE}+1wy|u_))Db{%?*U=Jj%WvChrXM%H(&u$ZzKq65~dm zRWiu(-K_6CNuv94UuAG+@`PpKh|Yt|ozs=K!?r8Symx#{LFXs(zGqDrcEXykkrb2N z!7XQp@;JVEzvx}t`gg&#-04wC9qpztIX)b=h%YI*ZBakzjiSy|3H}b>xWB&sFH#Z| z&N9*-mjVT#yPU+Oko92A$-b=qcmT{UnWv5XV+`+*^s826=B!W|RDLK@;lV!6Dm?Lw zIkI_t8{HE>#?Tc<+XyW^wnAxj3T?#G+L-=jDGQnFFM~7~URD;$9YN>a_QHA3HJ=r+ z@K7dJp*{8CBbK+&t-`PIedfKSyb&gIyLd8TLG&Pz{_ecA8Krj*pRwY}&5npI_)V2d z9+7;>R^9n6V#bZA9Zgj4$iQHnXj`I7ceBS@V1B+qSAzt_JLIa}MfUZHB_?h?;Z@ zq8cxfExm8?yvYPSvTU(B+KSHHShZdQsW$Nejz+2>%2s_lM|B=y37Yp(^Ufjrj*V;b zRZ%MYcJA0M$+JlCnxRT{MaPJW;&@rqISo;r)nm%7dg2a<^~9A1DjT{qQ!O-#m;u^^ z1wR<)a@=g%?aq$U46q|9$AzwKw+&TsSs*3)Npeb=+g#tCYF&jp6t zI+V4m&xbO9bx>p7(`KlX9R&+|w5G4+saj@gWa^qvYdYSqSs-~O40?r;u`HajMFxF$ zYg%!J7q9V`Z22u}K@aGjb9(w2Csv<5>SQK=Z!^HZPEMoFKKsm5MpvC(aaJ^CCTb<< z@C-$+2DhESD0^QG>sX^%oQXB9)v{3Ydlt8~hquG!9p6Hxd93!ij)u}pXVx@c5_}8? zO{zH&RxuV%pXz1RvnEZhX|92b*2Q^J>`}CLn`@1$h$}(9c*p1CNHiBw?6~wHEAgq_ zzG_@kC+<-#;x{CG%nm%R@QJIhnOW=ns;Bg2UZzVa(eEFT<8&#}YVfP!^aN!syd--y zoYZ%qJg@8CBI`H34^(6CesDPW74UfQtKb;$Yv8FMTRF69wDg3HTWJI`}x)1+vV$wUBRn3e-xlMc_W*VsH$&1Qdgog0sM7U^~c)tX7>p&bqDE z?}0CXvNu=_*4hdF0@MlXtSoCC1hW3D^%(GF@OZ#@KLQhn>GAXDXAr+{yRmxAwrSAzcl z=Yj8nH-pUDZM_405Bw6ygxc0ez<+^10RIjC7!2qQc?IkXz5y11ogniCTmJ?Yfqls* zDXB1@{E$&u(o5>FaK71`h(S0f&J<01p9w3DV2b`dg6Ro7Q(h zdSY7ppks%FgF$*TTlWNy2P?o6z)ylB!Lz{8;Kkq=@JbL1s`U;Kt=akzh#qbIHi))u zeG)tkdRz{kLe z;0xen@YmoJ@O7{bd=s1w_M`k=0+xU?!GR#PQ0vFRCU6h%GEjXwvq32~zRP!Hh_a3h zQMPPDb%ZV35M|3YRD;;E4Ni&ZW0gIX4$31po&05Cb@G>`c}(OsHgcPUrfx))H}Kc>c;%YqgL#(S zU_7v7p43Dx_kFY7OEa(cZ5~0#z3YdFW|Ppf!oUCn$+5<4q920(1iB|^;g)7rl{b%K zDxY3;j5oTYAriIUeCt}x;$lsf9aS{Ff4p(9<}-X-XpuIL4$b2OTMlrB=h!}(_YQ0+ zcOFKo#oD~P^D@3Ktsh#J))I{M=sX&}E1vxRag)@i7Bk=Xf0X=qY~Re9e2a-Y3eN@T3 zH*>k4T{7=yx!mzDRo~-1q(_4hF?Sjt1toLeg~!u1`J5g!-#tbvs$0%cyDia!hJ@)n zp}B>8%bSnfO!?B|*4_B)ygC#}w{S0t3HPF{7A_5&?)Ce-gA-|2nm zT+{TktQI`lVJ|7dlDQA6h*x&SdtqH8nGsWZVY@ot)Ul?TO1gzQsLSafM5abG5_5fE z%>H+b+9-+cQg5QsER;1DM?sxSh{E6`C~Gd>68ZaKM7ucLEZ70w5B>#w99#>&0=@;V1m6awbN&vV0Xhh!7fmZnfJk)zzv{ku#MpU>y=3n9@MB6g({*f1WmG2)ZW$HThX*zvVK2w>x}b&C&2Cks)2A9h z*m*hwcuYLRKju%=!Q6R{dl@4E$98vp?%}#Voej57&o>PTQ*wMuscK0#hRAC>M|4s# z`l^4;36xL9%1W64DJ!Db)kEDi$hJL{e?d(7+q`>Iq{H}jT2qv8%`z}WszNvox3b_q zC{xTDRG@W*+kSmQb*%L{`BkcBwvapNX|XFg@Vyl03lsfh3ZsGVL%@qagi2-FM)MW3 zYOALqXbsgL8Q4JJ#_- z-ChrQ$M2wS^lfCz)?>j(zzXntpk{V_AG{2F9K0U<0jMEr`jwnI{0aDVelG&Q2`&a- z0O{#$eGOa&DnC94s>=Eqs6L;cgQ~t>1l9Mm96Sho2^61y1?od3XDjZathkFx1x5HW zw_;DFdKBz^m_e~m5EgC~-Ry!pn5Vt9FI@@FDjAc={R;=oulhj7iyL>gvcxC?BT_QR zFvdO@@qB#P_O;MUd~B_!J*h$LHAnI-=AF{nGRwVEEx{97CcARh*H0CY;n%!JSSn9I z$%Kae@$sl%-n^*$Dr$AnMNyZ`yInt>JxTO~OXkhfZ)+uFx(;l)!XoUKdH=wcnT{EZ zV`BI5{R3deLB)B8&WrGzxMGEeo`#>*9JP5121 z$%A7#`2uS7+jH0IQBCO^OU;c@0c+W}yKuF9JStc@YVl*kQhrg(P%Ct<`98?PtNBiy ztU9=#>4wgIsFOqO)H#<*m#3Oi)3dn5>)!%)3<(=DstIPmvcg~#l-0j#kXik^3d)Mm zU!n3BoOgMuLK81Is%!LHb-_K{7bOag!pP5`xwod^Oq*KY&}gM5jGi~fuFWVl;dsgX zSTLne1wm=N+PmLI#?x;yLF-uXbKrRJ^WX*GouE|F-QX?Ym%;CX_kmKn4}goouYx}V zzXnRFFmBko8vG_Gemn#!gG&mQ!9`gH7iGH!n47|xThiFwoH{XHV;-7i>R!-YW0w1k z2-uNlH3RhQ%d?;&u_*yZC(+BXwBlXW98v!e-jt7{i35AJlGu2sWnq?Hk9*DZoWzF7 zN-spL$2Jjq9TigM`SQ&Bm$w|sCO-6d^~pR}o_TL(%bqN%QKwht+44-+jFvJM*3b(& zqOwRwIIl(y*_s-Eh3iL(x={+_Mlc=1uJ@;FoNOef6Vfx;rK#!h@5$7w=q(^@U~46EUBMy@Vbr zxovSjRE;JxcDX*Kt~CX4i8jc)(IiAB5x&Z&W@UiPs^rWOUeXaJq4`MC?HVR0&}Z3g z=Irh~jO`OoWNbR%BKsOT%k!nYTb!`tVCI{U^3LOw_R0>;CD9%im>m3cCf~cnNArMR z@vdz)O77yrpYGYsnI-AY%k3k`K1W)@GVkkiLIW%6g6)&13wBY+4n8M$W^xhG%JTgEMLn<0s7|zkO6t3Y6mXPR^_Fys$x?f_+fq|U znpW>=ZbEZS<5V^~l#}|$Vv`KC=hfeE8b+j(Yw6Z&v~In)HTVk8Mk5X`wJkMGK`*_m zW=4xmDhMk<^;LzJ_^C=zlLIDu_c8R(h{q>^2Z2m$X+0D?6&wLpf}_AQz!~6pZ~=HG z_zkcMRONauSk5~?1*#wILQs7-7lT)V6Tqv%iQu=vTJUjjD)=He9n`?&Oi+XW^Nc<$ydAt9yaQCH#}~mz!Mj1_(=UO_Et-;KmAxpd>_w%5 zseGlmUE-;f+s4t{Zj0Qq7Yx{1u7UIig{$Us?1vDy3+Y=S8Cqdh<}AegMROGXR1DR> zpexz8@*HjziK3}6vYig$o-~L*?#j6(t+pQVYb$%{Ld9e0r3o~SU3w?qub&Gl z*Sh&6ed$6(EBmHE!8i=M(8kB2)?NWcUWI;t#~t*6eP+Sa!dwk-G()Mu%b3xlsiea`v&F_Z?Z%7PV8UvZTBFLrieWqPn$ zkq{f8Y!Kv`6Gj5GMbmX|vV@^5ua5>f1PimMvt$r$wBhL>e z#NVCR#PuV1Kl{AecZf5|osIPEC-Yo5qxT9yNpUJeyb3P_;;+(qw$H0!^fZqZ~f{Vcu z!9RdwKqY)0xzTo!RS8N5BXJXz3O)}t-?`o8Dch!5A=1GR(jqDqoDF4RHOn z&+jwacIt*6JI?mUuN6}@-mFOO6;U!zSy)Wb(s=%d+j~~bvAt(+brfnnvo`0^i0y8d zsiF>|iuPgea=)|>dslm<{m?$_gTo(a`^=nm{aXqi?$fzFRNt0@Ll<=xbUuCPq8{p$ zO{==K<}+~8U254$&FGp`H*Ln`U=JwUjeQuD?Z$o_ln~73a z(^MUAq{@y68xmnyLp8OxXmNoGzsi}8Hm`4(oV{!289Y!bxzqp_qjy9}7?a~IW$kBl zlJggBo4JaB8relN*<2J?*fs1LFY5aKWKmz5W~w{x%Rs&KIdDAqGw=fN1+W(UIXD&k z1vm{{4yvfDnzL4+C^J$-rQE=-0O+;;a=Do}ge+)j_&dKf~Pwc)7-o437t5l0rRas??gY+@}`9g?ONRKcOm9NS>gI3lohUrpzd=|pMbj8QMn4&Gdvx_UCOOXh3f>07UkD2u?QcF?qBS1(me~>?Mf{Pip_Xyd))4Pb;1*8bOj|>|4ll{Wu5eHJ6=aMLWnaih z>0w`V@G8G?=7+a06jJ&1PCn4*RcRV2nxn(ygW`b{jlSssZe3%s4aA>Qz%#+q!0F)W z-e$O)Txm9ga=zINqEhYzsC%5-kMJ)l<#vNnh_vhW6O{_o(PUvi@2OPqT`1-Ebnuv` zQo++uDh=u2r=Cg$%b`?$(r%ibC~T0vbb>jz^E`#UQMqajiHHn_Q&;z}5%kekD6zD? zjUWyCHC|O1><;zeECYSGSq2X}>Z8|lOab;g)3sNuq9L}GTCTYJESEew?osz4$|2o4 z?gFRJCgWy0t_jN6IS1-{F2wavrsKWn9NU9je@< zj$6gEOX7Ow!|S-}a}!Fg`rM?Jqk(j3JhH7*YMT`yTI_AUQSHp64ij%wMum0%gF5Wo zUA9bzA-x!>Wh7-@u1J40J1x^4j096}b2)2P2gLi-vKxw)f zP_^n)z zhP(unhMWmXL)L@RkUuB)nTAyUHw`HY`;Vu~o!jR;WrMd0kq$I?D=HOe7aa?$C0!O) zlP{F7?Ob6|sX!A|l--ObmKukg~X(U4++ zX~-3hLPO?Wc)x)?C&#P1TQiNB>Nk*Co^D57rc`#d_06p6&)SGgS6?k8>Z0S$y34U4 zJGbk6G4?Y#VgEF^e-7PVWsHr52O_Kc)2ysqdgD@_sutBVU20eE0_wNgTMu$4_w!a5 z`KjS<>b>lR2j5iJ&ZyPF$Zo7k@#VPamXRLb=h zncFh&FXhIq%}v_c{E-R5{YHJqtZk7{cl8cASF&|4v)2f=Ek5huZJOPxxv$T$@L9*g zWgT_&%qQ^hIadSi38e*%h0ap`6I7YovHeX_UB>(&zOA3Ai=p!8izz%+9tdTne3ABU z$`{I2hb0Cq6h?kzsr_aT{NRoM?Nrdlir5s`MSe zt?Me}1wR9r>XxNm11cTYdO5xk$x)QaQB=z1aC3VI|K`?HX0*ke5H!&5iS8C%FLI&# zGT1=E%3K%PyN=oyHdD2!yF{Qk<;$)G>oe_1jZ#yi7H@kj<_rKq3k zdM|?vsQBl4-giX27iGK`WxO{x@!s5e^4^!1`5$$8`8;K#Fi?5}ia(^*$dcEH7${Wy$Jjz9qbJm6sd6tYE4uFAG7Hmo_i6zeX|>Wik_G zGBY>H%-nj)th<l^5D-$Y5XMF&T`8`p=b@SGP_E97dZhgH4r}o4pKf0aadZ z^}PRk#CuW3dr`)Fa})2)4c=25+|O^O))z0>PQ*pe^ylw>Bi52O8Xt2-|t5J z7G?YvWgA{#tHYHE7OEqYmv-LbnT2=}pKVrekBxjkW9&*<6 z0ER}K-2`JDvs_IY&y|W#+)|uFxOLs>x%EX*>A1^tX+y*%QN|@v^y=cv+%$$^ZrNPQ zpZ_++rCGc&IWZ#6rPq4lQj}M=kWYUn&P{RY%brX3crM-RxzvZmnY}2=xFpKBWNzY; zxyAG*Wq4ub#`#QCEYeXHxpEZQzgK%;CeIH~lvRaMo>#f}eK)TdwH`ja`V?jiR!^!i zt6VYWxO|~3=Fv-o*j1os=)AOn@;*}`ll_=wt zxrtZi_TS~zAGVTL9Uqcc-}b!vj_1|Go>%FJSE7tpqKsGOCSI8vywbE(w=$svR}DY- zi5wR_qZ(o-f|j|~RQ~EToN^>3pew1sGPs?Q$ZiTFWm3j{XZV63-<+k()j6V*x|w!a z&ANgn%ZY@|b&(VEnmh60s%Vp0@Vzpt0^SFewhusM>C%X;qKvJgQo$1cG#xzash;ba z-3Hp)fNEhy@%+)@5#4qeRWyH{7RqBYvtT_!AYUAXV6&82C zM5~$Qj<$%ePi|E@(xod~8p^k!G}O^QHj%d#Mt+Xx{%81ULB71*#JaNPrs~<#n(H=E zHLG(l#%;<%phG8>J^c_1YP#O|t*U7pcX*A25VvLSjp5eSXD7?+eL-cK0&s6|8&F-= zh2SAz5vXt37F1-rMp6-FQW2F3UiMGZZtAzWAr;oW+S;cMp3^;%tzyavy0t3i2Hty8 zLJaz(P{lm6$wD!OnaI_ev9$>ygxkcpEbsc4#FAOulYCB3S_O()tXEowaO*0DmHJ%@ zO3m!xS-X40T2aPYQFd~fxxIjYbF&QZmMiq&tWveNB=C&OF<_34e{x~vlQKn;5rfb(GGzROEpDUkVxv{fwVO7!ms*MXPHs-U$lv1e* z-MW&!V{>pz+V%<~E9e03{iF@um5;m44Ko^MW1ZDBO`0~%F*l4W4C%2|lX*>n%))6> z*11%QGrUBvMEk2osII4UX?EW($$E%B5;>gU39+vhy+q`Fr9 zK|k#k%cIV+wr4FPGpYN)_R1IN(Y8)B@19txQ?y32L)K`I64v-cFKMq_ub$6nAFc=5 zsvdC1dOVPs^T0MGH)>)^Tg3w#hs+}pw1sWIuB>82ZLvl(^V=&|y9BjYt%y#JXs=um zCbDF%jw#fpr=g=;5L7bv-@G_<%5?Q>-md#cK?}PHm2zyKy@l0DOQH!MyvD~@xroUg z<|D?Fo!hH^6oqUHIJR_+#+=(LpUCBV$M&j6vwe3?Zm)be>9ypsWI_eM8PVB)HXk=>ea>B&$s?#kRYnG0QrOL4P_{-1_Anx}<Hwl zu~3ION;^#->L|@NJ;YIUP)9myMnuhmI?A~=rbJUX{Q4u?7Qu4?~wEROn`Xie2Pe%g_4@j)^vO)Y@nl1Zb%G`DPnPPBf} z7ibvvyXHy8r^?@Tyv?7EDeGUw&@JPj2+Gh8ZvrWYudu-GuXwF`Wgzzp-hD6X5y@e1 z@E~v>@EmX`sMPHTs<+|*@OJP}PzgE;`~i3jxD*@#z6Bl!YQMVULG4sH64Vw~qd@8N z(cmobL{Kwn#()`+RfDaXb9gfN40sCoXYe#|Em#R|1jmCZzT<3AtR6<5wFZhPtN%pV zEJ;=@n&LZ>hwkgEHJirGigFS(?xE6ubIGZnGmd)v)GITEf09 zW*NC5#tr%iSCcLsRiNrn=8z1pkwjJK3SzR*N&&}8jx&tnt=yY#50naK;z1OszK@MAV0)VqYHQWQeHZju z^D83K&jH~|XSd-FeTYTTI%tkmWL81Hs`U%1)dx?gPT zC8o6Yt5`=-cb~LYyXA%9r&k^9| zTbPS$X3b(VQfK8W+MZX=UYfAUdKs}bxN1SFD|fY^Y%0r$ zL%>JCv%v3x7lGdgmEtGBCh$pcF8Bkm9efH@6+w<{eG_~dEFhjGp!P5RDJUcBSx_6J zJ_kxRKLf=l8DCb4MOi5pWruc{o2njjyO5CPCWFk};z!T=O;AhE@q4-@_i@WC>X%uy zfnyVNAi~rAGOuq~05PX1XdXsM?pMWe37QkcpOFRar}rrs@k=&E{9wnNg$++*3l4i} z1btw)EoLWG_ZrT#l=J@DtZIX^%(W*$xMlJB%$)V?iB?yTr}(ZPfpVFTM(;l9Z0qhiiRWgpPyxXOJ@=IMeFmFxKoEkn(rGxWikW0WeLBbCQP z+W_e(t^N(@`;(sIgu1@tHts|M>2V6rsIw>Rp2u0Q>p3Q_G^RX?-Fy^N56W1da_T`@ z%hbaiP#5D?7JLKB{Cx+?)X1ZezduAzS3{lS-l94?sbsXw)83+W;b%F2`$Cz&ktxg0?HJWR*2_URoe1g#Y$OP*KKfSHxyNKWi%`H@KUUH#WmJp#F5pnti5MuF}o-1SC$TGm@&D!Y1);w&Qb4_ zV}0#xbChb59#TlJnzTEYHeVg4!1Xh(sBV}zy>?P_mZ5tU&$Q~1OP$=(S4WVT`r6Bz zsp1PUJB}GJ)1C(!U6z|!xKFr1_DB3$2T3-K_I>`xj%-iG?{~p{z%KAGkTTn<%Ie>s zswx7xJuCZxU*abv(Ct~-AN(c1DO|03cN&!X+8$JjDKxFaK+1;O{(eXBB7W}#P67vl zbHI;*H-eu4CC}YKMO_4fj+kI45#DKXQ{jZT{4;VqvQ}PpE-^ z&Fp}CJX^ij2A8{&&$8Vo2RgaCCMfsY!)Gf>g__Oz-Qh{z<-Dn!cg_eV$6VF-+DvTW zJBC4p8_4HA8C;vr*AQYq-Z#{T-j`7O=XiVg)_mz!nC@rYY4vJ8=f(y$D=(&WZBxme z+beAo7pLL)VcL>upIJKKpT;=XK6iA6W0G*yDhYREsWB+wy;j^CbiDp)CIg zoiOU0iEQ2G*gVp$H&@VL9f8Dw?km?@FOA;X*yF97RZ=@k({g&e)iqj*mQ{4Uz{Rvx z0T~;KT;pOL3Uw8fZANsZb2}cl{Q3AKo{D$j zrF^X3WpPURxS9KeL_Ss+`8k?y3Fb*Z_vsq7kLh|=hbSQlG#As~j!1CK}Pn6^h^A5zH zzB;^=^WuFNQdc7rIZt8aXLs(Y`E%Yi*;y|h3jzI=H|y~{nfnxPGWSg+*Qpd>Cb--u zJ!QF1l;u8A)^TNS>bNqu1ao~8Lc{E<`ZQ~zb7<{rxy`#7ECkYleeS?s_3pTWkFJRl}>)zL+*+X69Nu4vJ zT}yC5CA~NL!EVI(T`&s1ukW1MdC7IO)Ch2ycBNiWwOZXctL^2)y^b}*G{Ceo9>Le1 znQSnH2Z9$Iwy1Nkj`vp6YC);uW?)BCT1evpO=;=KEGnQyH7M4iDhR#|Cv4{9*P*N^ zy%AB8GScN#hnEX6buzn6jggU^FUfiHmipcldO!C!!C<17bN>%0W&D_#a~27e9S z0lor046Xow0R9$S05cGq-ypw}i5b9rHwih#58U{d^1D2$PH{Thn2c%K@Z2f0ZJGUb!vI zak;cy(wI>gX*p%5Nu~U8*CvdaFoMv#VNR7MV_T*~4eze5Zmwe*sLLvnfDBNRfGCrI zD6i!bhM`~9)b7WDwRIbZYjCm*C-Q7y4>v}Z{RlBj-ikV3aTP~6Sx9!GYE2v3JO;{! zEGwb%Gqj4QN__aeVyIdk`YmEs`;6$-ikblZnL7wu$9LmIv6;I+PoVdYOsmP6$i8_nbEjFv# zFruosX-7@Qh(zg*x?RasyKohw6E}PPca5kl!*A6(<`|TA@Y+B#8cI7?@-xZ_HphTY z%i<9gWxFkUcg31-T)TEt4Kt3-A1Qfk5&QoSVKzndB5fL2qRs)2uOxDXL*Mc>Ez8peTvUL# zYcj+RNvfHp!j_?sKWgih19gNC@b8eQo-N+({%8Tr)>ST z`5VYP%-={)*?vCemaD1X?&oKz2<+WXwHP|Mc~F``w1G0?=WtPGiaUQEFExYsK?$|z zR%$pAri;kki7>7YVWB#};+4Qao?1TM7s|@Tu~1e9)OBM|FNCsO-U^jJmrHs@{s!D2 zo^noDMcfXZ+=^Xc{zhg3=zNS2q97*WRXtsT(qLHU!PXRX=d*L-P)5EwE<2w8b@MMiq6|;*z-qH84 zMOni<&W5llT{4(H=r2L4_}j#=tD(|PaXtx^Khr$TQ)!z}L&>f{J4Tu2e(0oXR2cdB z9`}8lRE^P!_=d*G?AM(fLE#+{C03Acq^CzKLaiR8ER_% zB6vIaE%0;TBj6W6@#0QUnR`)`5k*->6qO2A`=@EQaNgWpMl4=dvEF(f;~kK$_i@p+ zuU2&Rm}e*}vQ0C2+$Da55^Qm&tUOD6g>F%oO>Y~Czh%ztbf~3{Qiy+W58)24Ga>X1 z;idc_yG(xH=9W4ilW4XojQj|HvdR1~wYI*xzQ*?bkHs0HMXwDS(t%H{NzgKocUSn{3tRm3BCjfQ10MMm!JZ1v z6{>Ryz8}9s!CwKL+_g(#tZ4@}$SU~a9m+0i#-4EsVym;Bk zayZZK?5iSv4wT7vBGeb1n`+Oy9Q7Hf{4&3jr;>ME=5dSRvph?lx2Q1ka})RDo0K_g z+%BtaY_6_vnK_Zk{ml*G;kZ>2k%wL?7}q%E!ugWvrj*O;%DwDvKerR6f(*O^R9HlpN+%82K5)z3KAVQ&tlj z8fMgbRjs&lTzQ2g8O6)1(vmE%yC4IlZy=~Ne$30{Rrq1$Rg}p@lubA^x8?XZH)KNJ zEmL=zIn$A4E#o(4-XGS&!G6V=4mxCtJ733nX&=9SJz3%H$9r_e`6?Zv?-gG^&7CxW z6h?lAazD1oGC&u6J!_6@tFNqWY}nL(0HFb-DQOdHr%tP<0cIw%Tr_DekHxGsiN8ws z0o=LAT99K|$3GRp?HJF>@({o9WG4q5kBa z-UanXM?DC&+EI!xnWKJy+dzKaZq+;+l=b=dEMyOK4R zhYc<4@8AI0GC87K3jhX)7h7tdxkdwPDH;X}5Sy~Iwzc!b@%1~mRXxagwDa1C znRmF(+6O!PFLe&XEb@i!$NavD^tDyp<(AIfwWfuv6TY!z?j1xCruv{ns+Hh5d)kYZ z_VMr6p~UVh+bZsIJ-~O-zaM_j>51=|oBMklPYASd!2HrgoZX-GjrOYBj&U(H?ozif z_lq3ssoJD!+viDQ-Q5hi7N^mJ5goUsJQcGorJZy=3VTL6ioNh+8uhLit%#_H+~vFc zqkX$($-~k>upEPg<9t*)k1%Bl#K1S93?g;D}?8Gs_;^gx~`%VVsEMg`BjB*4e75+R0s;A zejVWm4Hvm9U&ma}3L&;gSy6{qC}n>8ld{yeqLZ?=s0!F3&#Et^M@Mm6lTI$G9J;6O zs|Hc7dWu})bClG)GF_IMm!oo}YML;Exy=J)2)L!}zFncdZYd~vY@&gh9-xf<^2fS^ zh56EI4wL6I z>EQF=4De+TZRfhf8^Dvv1GB(!;H999gcguFEy3mBRp3>i7JJPBzW`nX-UnU_J^)@1 zej7yVwEha52dY_l1Nc6O5@{X8ceR0gf$iW3@Mcf~`YSouDmqbC(TUY_@D`NHM%tBZQK{fxxT##FgFd87lx;ArGMRQ8OpBs| z<*B7*iKl#@NACVIn~_R|^6sm@zw!e=L!Wlodg2T^cl4XYMD?LtEX3rG^$Yrz zJXZOE4@u|k{8MehPjR=BTwLT#$&sY3(9L-G7gU++U(DagzJ_SU-6F-$rH0!lz_9c$ zf=;dwQyBTt;7X$ur@Qi%{!Oe9Q{+9b_z-#CeNe)p+g(uQ5>7&|ojHq{ASyp9JtJdr zDE%ru@do+rOw`sl-2om1egQlmyc4_x{358NN<&*N6lJ+k6#vNs=BA0`=H?oIS(|cX z9fKJP@1ke)YFki+4hZ_v?6f&s`30v@8C`L~_2YLaxCfvwNT_Xvk)Lwz8Cxj0$t|;H zOmhvznDXX5IW7q=99?l*&+RDWde8Np-Qo&`Gc5KJrBT9BI)`}on_#|vw}a*2&0ZMF z;3f=FCJa&5m2GY^)65NFguNqbeJ|>RHDZI?3v3L9wJ#YIe;pwbCZ6B-6$2$KnOfJw zI-#X~ZJKRtM#?i7Lw*H|zQxv_C`*kcWrc43omKc6C9>sj6QJ@-TIrEg!;+HhTX~*m zNl7b=`Yx1(r+pNXRswonc_c02E46A~`Ne1|nWg;f7QQ^GdRHBoH8VrS_5ZQ=E&y6p z<^KNy#&LY#Nd_GiFC{9ec?G;7s14*KUM9gr(M&`@!4Pm5yrniaj55BwrR9{R6c&}G zmUYau#PDuKg=Hr#NKSc*X{adih(-DTe4l6SJ$v@daIsGQ{{R1P!#mG+KkK^Jde;3} zYmMiZJo*+%1Zzk(S0w)RNPxcY!%%%5`DTq?EA&0Q-vB)ux)7=~XoD(7Dp@WuLM|~v zUco!BhT`iyE52;=b8d9xI*;9>+mBe@ zaSx6#>E;6mQ@~+Z@!DbYclKSR{RTd;(EMj*PFE>jX>4lzgiELstJJqgU*|Vn&i7Bb z`9OL^^MOGFANa6^u}0s(;VRN4k#BZ_Q+hg+66t7K-O8Y3JjT$h6v+qYX+_D=N|3vd zjO2TlFT>N5$WMOaUg>&nu9}h?6?gSMAK9YtPMt9w)xUUn08_YeGS|+<+tLAMz0^{f zq574CpFE0_giz_D^w4t)Jrl+AlJ?nWO1<4^N@y_J!*J2(zsjbqxmy@dM z{f}2gw|*?tlT=T&VPAM}A&gp^rt7;>8Xt`!fx1#XX75gMU`(dw;V$w@D54J%r%T{)zY61|dgUfR%$iZC`-_$@`_ zOLmx#wr{UUCMlVRSTUe}B`b0zPV}*CM;)A5%+-5fLJ*@LWrhOn;}t!;zK`wYKOE1x zGLJc&bnfKyy9VPePaN6R$$b1~YDasXr|upSvCpB|yrK`H$6(+Z$)|!`jAnwoy;BEF ziBucz0QnBfFQ)ds0on!AN?UUD;jcROKK#`TmixC#JhP`r?`%lf*;^H)VyPmf=S4nr zANozQtKuf@th}hIxbMSH^wF&z;Pxu23OadI;^Onq3~<$$$}4rQx=+K4qiJ|4D+9wA zK2r0Qv4K^6r+j-9@s4?>v+Y*IW_aGz#wkrxXISTLCA|6g5ZiBU&3tE!x=sVu`={f3 z*H%sIYHz99e3;^-wEZshAn0=FFz92@qoGei&wxG!JrDYQ=v?RzpsG%P2)zlq5~^zS zC(wJKKZPofcR~`pjTiD_D&zx2$%zjXg?ykWDe{4$kPj3kfj&?aDg^EJbpNJAR0x-* zJe_{yVJu8}i&7p{0qb$AG~NiB%j{YXj|I!?;qcBb;%#>|{NTdRFpWZw2<92jE%rUCY$owBa^&O(?tGnlvN_OUIoauH|Sc(PvxV$+2!6d(wkn z;yi=w2?eIQ=4xu1PV@82jsZFE1kfe~s+PlcnY5y1uQe@)J1T`Pg4cI0&&ITdpIRP0 z%4KF-(_C%_=T~#(Ms_aX_t+LYO_$A|GHd2VGpAE*(I&DCR)D=cbd|JrC4sY(tP*v{ ze34c7`P}_Axv4n)26_SXrI@>iq`52Pxhv#F)_E$j&a+(YeX16V6`1ry zen)mGzFg*P4R8l^H1C~IeZxAaqBT6tP9e`uAwP)Ud26D*IP-Z3-QDLN!cNlNP0Aa~ z?;E^8>fE8WhP7-iuy>W%6Ua#pB+_<C>#C^pd?F*6a8%-$CiR&!@pV zmV!_l&IEZ)F$L7m>Vi2S*OvYu=<_y|ZUv3Tm&%|XjQ4Fn337p43L0gm_koTz^e||o zp=UtH7*dp9;Ww0D^85^N2*2cWo+e(|T?9MP8_9PEjF& z%nce6`bb{|W`^1DKS~P~80v~}p0h&bZoac%d*}i1c7PrZEkfn}TcG2i1ECi~2SL>f z-U)g&v=;gy=q^wdtfR?AZv}O1wBJ$Q!vQV@${)7tQ z=a@=*inc>js1SY&Px4j_zmKR84n-0?+~E;%OL@whl=3!HKeLoUo!m9spgI}7a4k>l z*C5^Uwu0TC#$?$>8{=+}irEID;3tc1Xq(V;`J%#(-0(_TIns9}-H55uF_Ag7qnfJ3)2dv0 zRLLsgT=cL^IP;acz9C~FYY_ikKQ#eNvcwA8BUI*?cK=YYJ5p91nKUR@%u z;;#XD6~7Sl1#|l`&?zKrWl+WLRs2$ri?b^Blg;#D&^SY?*iSO_9O&JKB+9Sw8_F+v z{tIvjzpBW{SMhHr`|?%1+^C8d{qeSmy~(t~OCH53KF$9tRs6H`N^H}F<|!n!s`E2@ zbGD(@KiMPNSFLY@n7PTRM^HJ5<&FUQqR+-Ud~*KLo09_JzJ5 zIuzOj-5;uk{s8EW(04!=K@WoJtLh2Fi=vPhMIo>LB@G0}b$g6+JiC6zZ zUj0k5y!sdN>R+p8NRY0#tE0sBkJL%WO@s)aU2V}=x z>=*4CY$cP@G!yIS!DY4^B_Cha z8unyJre;`;oS#tHcT@6V?A7v1WLr&%W+C*hrbNwlzD&uD>?p6d+~4okVpy3{c)dK7 zsiaxxHX`dW;8ysORaJaLqeq7#TZ+?RP{r%;C~K#a1}+aC4KK^WXL@zsSoel)TcMZ0@pm%~k8&#aB@0L!%J(39Bo6g>@HzY84)Rhpg*Ri2!g3bBw2u~5M_d^vA*wC86tJ8uA$ncF)g zqGA}9@=Vx!&MI-xW~Nq3C7vZ)N&Iw5K$Q5n;46t|A*MQ6h@qI+0huiB)^EJ2;Z^Ps zi}Ms4JJBdzf3P;$Z<@CUN5a_gpc@JBrs}GNoB-_885=uq!6ky+eOhWSKG0P}CV;$~ z{3TG|b=%i4t>LF6)RUiXdL3T=Iv2TN?0K`;Jz&DDuzGoV7iLv; zikN!97d6hg6v6T(3o)Af0Nq4}UD#`*_|L}`>%4JcB3lHeVsf;{K>cYQ%VJX_^rz)} zFT&nK%=P{2`DMlYG{jJ8(g58bdOGwF=me;OdoNV2(M0I`pl3s;Kqo;}7oP+D5cFKA z#Ql9xiTn9brSoK{lH#0H%!ORcg$iK@gw}a)iOAb==cyfcdx&(R;u3fCP;L8FO^5qJ z{{T&tNvM7OpQ`iGUl#^2VABypn;utRK{q?}q zg(}g?=%=6((@#U^K<|L+dly3$&iQ!m?X{50fRHa#Iq$h>&*t2c{7%~E zO)VWdB!oTT1FjfL4!%CL9qVYJRHwCC=OJu4N~TjQI^UM5yec|5bSh)BzxH(}8Uwzo zSH5K*m53R{ureGDqROT}r4%^J(5W&tq>cUu5VS&~!u1plODB zlduJtil2;z@)GtEX8HT@@-v=pwi{VER73|st<#f@upa}VC-^%l%Y32$Q!cgir4_9Z3c(|FGZd)Y( zlx~uLTsO%-tefNyd5Cxhwsq__<3*%)hTiPYS|OGhx|a(vvyVzeBeC~VvB>0JrGol` z@^LDNngOIzal0Ya3SaCm7ngy)WJop8U4}jga<##QATK>PgD$f$ZUxOXbQ@@np*ujc z4M|}0-Q6Yd#INF~CtrGg3on1r-x7Fu7(~}?vGmwEf7AcZDnQ9@#s8#4QKp0Ub@TPU zzAsT8j3c* zA>>=TBp1H5OQ;Z3t~^|Ils#PQ9*>m%(lZb3h(EpPFV)bO-DdqIP768R^_Qyq)?Z4d z7uxEdL1U?0|NQ8croDt}Q~vD4QvEYM*M7RGFOAGB19hEBAw8HOe3zbW)L*(DCl`?> zwc*ns*I!x+@?!aQ(A6YsrDd2;06qcF^_NurxxH1OtIVFNlq(IXNal;GbdV%s5)(Z; z4eqJGl)rf?tH1Qf7K^H`vhLQzDY)cGY6#x{8J(r16SiqjI$_hBQf+XxWnDTI$5$W;UKI#=RUlLd-^5gMQnWQ8A-@<`Qsfuo z3KeW`rzEgwYeGVgzvR4yDNp)K&af^Ay#M(uTsJ?9I z10XLty%m#rm`YY9LVDDq)k0MG#kc*#C!FISfPfJG5>rFJs z`_c6^^J|hyoDN!t#=E74S|$%|VenY~_G_x*Z~vy(SxpegH&QCqpLzBsn}a96(Hu zrySLqQo=37zSf3rpD?6CxCm4kE&+MJK?4Anp#`9lncfV#$xv_A;3qIe+PldWnV43C zzXInk%aR+}5iJbik3D_K)pY51>co(!?mY9-xqecPg6-w$IWW$RlXI`h%-|2{x0v5r z#8V>Ls))_xNuYj?@w{pDiu}BV&r%+;cSF}@VxsSoTyDqj$jIMiKUebK6*>&wUeG$| z+n|%7dqb^EkkVcqwt-ykgbH>=yz-%F?Xr+BswkKI%mpDob3ys)XD$d8!VoG@_fLac z_Yav(CUbnNC^t~4(%a;fpQhfb+a{=~)L|s!Q25skfR9yM*1kfNRof@eu>gsOK0%MF z7xm)BQ)~DbzjJab5^^IuNAO#`mDLM-yPLSaC%O7BzHVkhE~^f4JAs%wgfdapljm^u z9wKxhX6j0ew8fl{H9L5_YwUI)tmM%*D66TJiG)9&r3z6NFNsx&3Z7@FkY}lot3o)h zifnNn*>cJ2Gvw2>JN0+`_3N~!@2s;Yl$VX8iGHXPW*i)yV>XU>lIw;yO#8fSw>7M1 z&YvmRw(^5mF171#%kOt;bFfUWdknmhTnwdFy`=XguP;rgv6{y!9U3U=osK&4M>Wx) z(wGWPnMeIir|(7^-S(E6^+B>uGpgf1SDLM4TA0&q)J4;s#-)ktX?o4nFByK&al>7` zdyE%E^Yz3>MeEBnmUw3Z+tzSf+r&G{^|z(-jan>Dys~IrE$b6&O8p6cK@u$zoKLH< zrId2(EJDED`qIfUX6@&}Iip;bfyb{^oZbUTqv*aXij@|ocQe{rPs3?jc|3p1?ZV08 z4M~{MYLdGjDQ6mLxY4m(9*NB)x)QLY3sP<$B0tDz?tJsem22QkD3@j6oOW?~4=0Ik zR{#q6%rxYDXIiF5%QWVq(l?WfGTu$bv4^D1UnHePLuP+N)+H6=Dv01FZjnyD39+_( z8C%iTx=y!C>$}_9*8|JOunt$beHD!EUn|=&=T-nc=c-~-h+}Ykxh$uS?Jr{8*7^jz zmSy@InhtTfD3$d}^t$Yq%X@KRg1KsIy&n4XZh`2Hk**i~KW>C{$U3g4pE)_NDPY>fEDx5DTytFrCotGoc{nUf@k zI7xEIX+Alwf!DH3e*=3(V;?n*dKy7ReK=7sTM*{SfL6I`YrT@c<#Ot`pVK|WrMP6K zd}(W~bB83n=8>qvJbk! z!*G)^%E^&T$=D49ZvM2j-VBr+$pkrC$7@E8WLh#iZLOPx%l5ZdIqH^br*5B-qapZh z!I5K?#6FRu8u(F;YJuf;F`^s=V0SqZCq{asL^`c0`;?>gIcsC9O5^-Q?d}q{s#P{u zFJ8-ElRJCu+VN#acF*6_jAW;qNUmhGdO#16Og~v7c>*8Z@t{7uRWaA(OY1^X^Xb+_ z{8fIwc(2}9EmpuTl|7}ro0pxG$Icn6TNRycaCX0(4VO5}p`XCjQ~tY!bUjYwAdQ;j ze*v*gq(>cOoXqdh*&bJaL>R@g;1aK!m^8aBN8Gp5*GkpxL1E)TsFzD3`_08ZS8VhYSrJi z);0LhvP@Cw*SL47EHJt(<`siV?H1l+{f=!HgChBjK8j>2qwawh$yK;1morB4#EqT@ zrC)xm#bN%ij9r(!x0TxE#~J0;HAFS(K2gn|$twyf*3~Js%CqsMvaHeyKaW`~F0xi? z*Wb4GwZg6Tx7TL2Ti&0l`mDQ(g2N~|l{RMJSNfsb6)&l=4d^Jw&XCdpRyDu-ryLcf;34wyg0I=JqF z*mv*Ke^DM!Tz}KpehXX2BNxrf^ta!@1=95Jm;Oy>IjCsbuH#gThs;0G4`pZ4DLFGq8WO8Y2G$L1 zYd;ZwY)r-h%f^Vgc?gUNNf_m_UM&s%VuhU%Y@%tLw$^dLPS%xe1lIlpZwOcFVmxvS zD-rc-w_hsDRa^Nv{+8P9Z+2=l&DJ#Td{yVcY7qkZX}isMt&eQ;% zKQCiPL$SoI23v{$8bu}cHH=E^Y@!)Y>}Uv*@KhN?sE? z60an!X3q=ruF!S%T;|*~W$FjQ)JEynnZ8{wMzHiR@1hsh+}`Z<%A7qV5DUT;=mXyz zMd#l_)M?zn2)HsF2J+)78bL1`@53NJ^KmI?IJ{b$=yTrp;r-osYeCPG{Iy{N=uks{ zPhAwKl-z~R#qV#Xqe0sES{uftrdo<~|8zx~`*$nI{aXTZ7x#kPzwd(Fzu$pQH~%)I zraD*B{TmK)|K0_1|4s+Fi}OJ4-(?{8ZxLu15WVlT~8_jzxs2RA(~ zMJIu3&E9)cbPnk4=3h(7`zWZ|?0pX8Q{gXwJO(d=JdDE_dV3hBrsyn?hjC$wW`I15 z4};vlTS4yMmq5E)XpeyQGqf6{v)O9HMv$k$2zsUN;xv%Q;6{+g;A0^7)%pdXrlzXo{>Hh|o}5eS>}P6RpcryxHR?0L|y@uf1X2Kn+)C&<6Dl8%SR`RyQ& za~;Uzd^O1ZyA|Yd{xQho{0orB`DKvD`5z#U^INGZoOd9|dE-F-9aB>@ALOyW5#*sg zlk$F%@>KP>y+MrlYWf<;({DKL+}?XqG&@CC$yfbC zYl=Pza{m^C&cUbJ@D#{nwFaaa*xK+%kbn8hpr2!}GHe9-m+wT|JH+hm1@f=F7&HPK zwc$$8bLL_J=vhPB0ps?*3G%#r0;JO@Yr}tm{EK$SSFZ(zf(|tQjs|(ooCWflVJgVe zZ!ySY|0U3mEVOTeJoeuP{lIwN1$peh4|4y04pO>ShE*V6z}g7%1+1!pEL@stE$Asj zLqJa&It28Dp^>1+4V?&5j#Y;7phpdz2U>1u2I#wnnm~^jY6dMcv|x((E6=nl~D4J`q67+MPQSlyMPdq8W=^ght<4BZd5YUEPE+^;{XBJqERIMBSLN6P*)R%(5)&s==r` zH(Z|$eE#e!X3m~=5%)E4gYv&80z*f>`B6CL&5y#-Z+;Yxdh?@jwW=u*sCG`Cq?V?yf=)>x zj2d(tb0oWB%mgi=rO*F;{lBU4#)M&DS9?43wD%k_GUOP?t!BL_U<^`FITPVPhEjLD`rp8otl+F-m%>%AG?&_iRIC~IpfHkNnU()?nyB}bZuJPSw6 zFzHn@ktG6S&PfVFS$`CoHahUgrsgK2KY5#TuyS^nP0dhdLexZNCPd9;_7S2KseaJq zk7hUf2vJ&11DImrQQS1gnF&!-p_vdh8`?*Rnt1w8_eUDVPcR3W?0~hSFnGV7#Pa>H znmvu)4#u)3P%n~UxA{Lg(-f+l`59x`G}XC5|Ls+@!n+2GNqExLzcRi@U48kbsn)J2 zXhJp<1w9t|D~i0PmAfLZ$>&VuH%7~8o#wVzTB3*rcjh0{g{+d(puRZf9AGK!*>Zi<)@W>BMqG#k!^XNVOkVVa^XUd$s)sII?(CfX(>DBr@>!DI^ z)~u|{ny%BJs(2Ld?gyO8fRUKRf{Ri}e&|MgKUke=py$*UhloP9( z=R>cDE`hc{zXffDsx03GJsY|RIve^CXbFnad-EO8TcD3a*|X8ypIo>VIv)B7=zl=b za%p}E`YGt^P!vs@RSuUxKMVaF^a1En=ue?vfW8F%60}I4_AcmX=vSb(LGOmHh28_* zoB7Opq2r-ngZ>BfKIlm1-dM_Sz5@Cm(2qgyhu#Z)0IHJn9jNxXE`w_S#CM@8C{I8& z&-4`Z$Ixe>Di=S5R?wRM7`i9)S?E#FpFq!m{uHX&zn?*uLVphZ36!PWW-SiU>^2vX z_7|ZCLw^f>AG8DddFVQ*O2c~SI_O`Z1DLVf0DUX;73deCtSL4>1^p}ZS?E8YcyF_% z+nX}GR#b|i4xW&oTJ$O7ErTai2*)9bpLTl@6@u0?lmf-@ctnLT7D>Cqc<+v=5S|1r zHlFT25-NmoNZ4nLcUnY+@B`3ijrZe-3gJCS))M2L5m6yL3%b*IKZ~dku0*0V|62^# zL{tb*f&AOP9}$;}fV5h~J=qZz!pk7dX%@q)5fy?YQ?r}JP#IAn3<7DMvlw=X2xV}P zW;}~wXhel@I7oA!#c)JKg&@@*&4L!g*oX?D0kqtB6Cx^v^FW#zErtssDufS!G)Gzt zb0R8)t3jGAEr#nNDugzW=1q&C91$1)fxcrr9fc`W2wwteF0~l$j;IhG0BKgW7``15 z`3BPbYSGR~6XNy)c$#S~2CaYy6~b>onsY6N-$hgie*vjf6vO{SR0#bjrJ9E=hV3FM zgc^`$WQ(CTqC(gkq`BE**e{|&I1Hp&+F}?HQ6U@;(tK?(jESfaP6KJ?wiw1oR0!__ zX%4p-CP!2Vmw+^zTMV-!Duk;*n%6CcYa=Rzg&@uF7Q>>53gMF=&Gi<;?GY8i7eSi! zErzc|#Fjsh=6~7p7ZF?jK$;0=%U?un`2%TAm@R)16~eDUnjJ2N-$qmje+FruxENlE zi0X-&O*6*DP!Ulf3>E)byaS{;=3+QBBF5z)%@`NM2@w^- zsUXcA7sKfhA*UeC7$c_g_PHk^0c-j7X@4Aa<}j2-Fd4b zDuy*F?@F2ow|7lMh45}#8Rwl6Q6bDpdGk`1 zT56WOx!F8Il;8T4CX}6rgoF7F=cBgH@4nsR1<(3-?z`Q`ci+KZdi~oEE(3MbWBX?* zRZZAvS$-f${g%ow479|ML`Z#}%1{SVkI3);Q!k0NQ+Vo4RoYfV^|dO)1@P1pt28xm z_0B3oe*2l7e8$;jRdHwTrZ%>PoPy4s?K<=OL0`e2q5P6ZaZ(Y`Pfvc=+5PbH2lL7e zMK>J8?+k`>{%m2sc$Q94Z<^CMdFqt8h~xBR|Jjth!B*}>J6+?*88heEgq2de>z*7qwLt5-+SM9c3c-H z_W#-ZutBzxUrC-kildUBsxKA4Q%QEk`!uNH-Vn1vb+BiHkY|IC-x27%wb*wa+0e4C z`Yg_>UcvKp{YEF7L~K*$CLM9i0hpA7jidWF?au+2Y>}F{ft!`M%DTtdBn=y|-MNx; zxKO;rDTn+S8b?v8I3LC5jG|?QEt~jhBBv4~&)23D^_IieVams(x%{KS%DJw~dw+-7 znYkR68>Nt1loPf!hewU#bb{&e1O$bgJY8~@-QjsezFZv#gNa~H4Ag|!Mk>am;7pz0 z*lntID88z$=Fz;6&i$3O+3Ym6X_xG6MCs`rl9Czb#{cL$B_;G*(v#fxjnXj`s=~TI zQ~@0b)z;C2pr=C*flh=T3OyS-3_1lm9IA910oB*56uNW?xpWB?!k?mP(e99P-ay&} zx3_ade)Nv>bo7qfb1Cdw2&+cdSF>^8^R>P0>g1|`e57Ao~m$jj1O0XA(~rt(QLIHL+vU-O1*)6aiyKS=qf>H!K)+LD`|o$SyXzp z;Wkj;HPz=Z#S{BMtfpcX-{ktw(4X+jUqdT5vhy*1bC3>yHs5e~hb3t(V)tdQuv3i! z@A+g!IlYEf)4pmVG;`ZEPb&|^(LAke05gq^~u9=nQ-+@l3B*PHmxF~JCQYg zC)wS6JoF1t)em2SO2+SkeiZs;sHGu)TqdO^=Q1f&2(?I<^9Dy$pbIvgE|U6uNteqz zQXFTQ+mF7fWJl}vRt>3(+f@PdY-n0B&tG38H>$9A=6CfrX5UmP-fU&leieRO)1H~H z9Y&bRqj)IEvyI7*NJ_=+$56#@WlUP>|9R31dD05`iQLXxfqm!oOu)3fRdjFAcfpP3 ziv9k@zKe^(jX5!GO4F3B z6!;z$RrkJZ&q_z}%2zti$DH{Eba%{GK~-v2Lyv*3f$BT{3tA187Odx(kms0CAv_;V zi(yqn-OerbUe!%?6@8r~OOGZ^Wnhm^Y#Y3M7e{WC(ln6a+HFkf`SYgFn>=Isl(}1( z+&Qtgv4T=6c@&qt+`mk!DSjKEisLIW6{YL!sVL;BDC9ScI&UTRo!3L|CDG$6X#FQt z(faq5p!?xLBDTOD30i`8R<6$|H;SE>#W-m*Y$^oi+wN7IP=;UAo`VA13bjgBwL#rGDL_mGQr{pDl)Kt9FF2V)??>-Q}XJza&go5==YB5p^^tg(>UFsZam(!Ok0CucSa`$v`=mRo$| zDH6PzV-BcU=^%bZ)xy?Yh(@vtv%$l-$(S9(6u6GT;kcc9lE5V$Nm7;@?(k45%rwc9 z^29~)LyTQ+SY(vI?{Hv$IV;5vTwK^%n~j2pbdxbV?xh=>d}>CjJKB5aBbf2(_l|kk zxUXNwEC@H(PluHK5F2*TTKFZO?ABE;y4(I;^3hYed?oQQJERNU*fa*^))|-}7ioHz z+oes#aWw8{=N{$OYFR0l#rJS@6bLUF7GVxta40D`Ls?mFA1^;L=4Cs|$6TA$zspt^ zkBznFpCV(H+FDNpb`C5VwKEjND3$eh;3I=LhGD9k4SXOKu8M(=tS`08r-2XjPs$q1 zXGZ_IY3Ng}MGQhB7hNfGlNogELbAAHU0G?qbY5Swc=`GKdql4v>EE*PP*n)p!>X@U zg&=ITjGc$Guv{Rl-hvXp<15(g_yR7TYTco)ILV;ndMr{#2R_os*>@D98cQ473w{!v zCQEDkV|?4CkL9ubS~gb9udzIJ7ab$rBis9}O1gQO{<8ObG4c18{!KeN7|?WJ$8TY$ zLK+Vy>VAqw$A8O;7p3x%MAo9fCnI!SrKzk5_H-|HluxSZSAVhRPe|s4?Nykk>6ChN zQ>*wLAI=0JmAWJdHI8Y4ElVqIb3we0c^X9O%}uT1ckcTje)+cS>Aj}th~-<_jV-mG z%p_qIyRm6J?O8*l6D&2zs>olS!S`hB+>zlg%3zh=#2*!r#2*zxC59fAxCAR96Z`t& z1fznn#@y`!8J8I6w=pX^;>ovsOgkx zL^HeRcPhN8#=8V`p`mL*BS5uwcbg+kJvmyJq7%pzP6Wr^T+pkAJ_mX$o&DPIT#70w z^>xM@nWFcAH0NI%W`VSx$uXlTDuFb6P#bPd(WgOs*qHPiAWc-)h97|3mkyBoqA3hV zRUr38`rj8TMP17y$TviXmynU0`S}XtJSopz{r_2fg3W2GDtiHiF(~NXMs~Ye-rqryG)%$vK9q zK$8qrgU&Wo13Jr4Eoh>l!Jsn@?FD+Tp&_7Ah7gh-OJ8SUs;U!W&+bfOWOw$dB+cs5 zW$(GO@4`8AW=)?mdy8B9CckGZxAHY+L+foT-@iJJLYUT76cR*c_@;n{`&g*&27I%?j!+>k9JO-7M*Rs@>H> zX>qeSNd0KPf;{WfKQKMfEor529G0|}sDrk5h_vG z8LAx%yFmW{-4$9v9u0<`2;B>M9&~T$bH}hpkIX^41EfEC{%fTICO6k zemL}KD2ffur$A9lXjX=dgx&-l1ziOl4ef-Y@6h}QD2f)%C(-+?gX)x-vCz3t2HMSw zp(jJX3_S(<5Oe~x19~P@xi$$Z?eKG<%Ad*58=+I6KZY{AZ2l9J-7C$1hO!7`>oV%y zyN15TKdzxKN6L^ixF`H8A!cnZXk=O5MBeRA6*Q8 zkEjr;$W`^Ii=jFqw0@P-#@jQZLf9XqUUo4Y6j31@1yX;z7>Y@@P#* zgw`}jeR{N}BSLE$q~1MR(-EOH4N^ZJt?7tRs{*OVkJfZVXibCE<40>cB4h~ZR^K13 z>4-=_kVXN;uxCUl$bd8+Kx;Z8w5CBC8K5;C5n9tAjSPDulnMJdIRkS_t(K6~oCXPvb&&+nn+=E_B`(Qr=T3@B1lFCD!e| zobnE0#N<5fR}d=LJ_P5Tnev)bdka!txnYf|Av68#)aD^0RE-~@s&#{@HnS$|{sr31 zW2o9)OZnZPi-~+?I2H7-)Kb0>CEz&>-lp4B)4uq=MLp%DYkTFTvqst6l$3YL-##B| zxUeJ$Yb zgGUsj^vUZUv|^IiwvijH9@2^0<`wa6S-RcQYPjN|J_6Nfs=E17Tn+zksN(nAxEih* z7+*OQ@|8m&*8*|g3hX}_XI46rDP&_+tzLW^j@HWVs#d{{vncW1?8G&p%&ObTIJ-G@ zcCTQ^-dpEvVd|_b*rATH={$Jg`DeAy-*t(uLh$G+PveoaSX-nrX)VNJ&* zGg-fSY)walAlk9xs@v=Y`lsy_d!^Ki_g<+;sNQF>lV!`c*10gAZf)YPThMz)Q5JM7 z#u-$Amd4D;%RF6RYpp&PqMh`|8EDSWjm4qnE3Ff$g)DydWeY`Jv-t@Q#6YFZkU-d8WyhLkiY_i=Tr?NT{16V!uAQ1q=b zpQYvJUO#J+d~H3Cze#ag9GeRoe*+tO9XCGI@E{kIh z$Uz`Wd1cX-M_D`+$k;g6Y&1n`|CT5!1m|*^DxyxlF{lZoC z3y2l2Dl(pQ-mn_>;wQ4-?w_*SF1F~HO^)?a@>VZCjUZRsO(g#J7acl7{lu&HQXz7F z*Rk=d7mp`|&gy7jZ>w9y>v&6F0cdo%+EzIIZ}$MqG#kLU7=Rfp*4ye<{q491@$Tgd zx|Q{+4(&hhV>R)|)ao-NhxR8;UM)RpHL-v9)x_i7BU?>8k?@2x^)U(d z)xxQbs22kM3!4sit$_Jgp%PHQuEuPka9NGoH@*_0_~BpnHf}W%vTfR};Si z@^!l>KzrJ1;tq((2#fQPAdmAIAWy$@KpyAGAdjJ`c-j~5@zZYjg~r@}BF8v*%xW z;q=BWE-P;Bs^Z*iSiPa{+=^>{QSyzPgZQQ`pLYM2;rSCAhv!dh9G;KqzwUrWwO(7ZNP98%O!3s$3^hFH zWzFy+Y<++pB#%}WHE_tR8D>`(kY*c?k+$|IW^bo62KMXbbAVzt1! z0GldkzK*N`N^~c0G)5^=`WSI!qjfj-*L4M^xd2^!&7x}7)+B6c36Mu?uNvdH*E74V zyJnr7huu5eiQ=XazsD`V0xgSw@3N>Nc<&b9kHrJ%_3`L~G`Nc2mkn38+PZ9M*p*4W z>`JaS*EDBp6V*?epFNacQ{-RDTB2ez8+tADGU)v>hjv1*fNH&vU7XG5K-qC+r_eE( z+x!jQTcFFJH$Z;{C2gA}Rb^-`a`REBl z8hSqT2hasjrZJi|A^I#->qI|+{t)^CRI5d+pvs#y(04+A4OK4u4yqNQ4yg417`Hc9 z(O3N=v;ch>s#;v56kn^=*wfc)g$m&=Oy?W#o`?!z4^r@Q<7w4as9>w^R~YYRctVBn z573py>rYw=6@pgIuQFa!M4T1{y4rXeM+gzYDCQ2gEWRNh7}PNY~@g+>0JA*WGDcWkHP$BFKPh*&3*gv8|7!J~CrWlTjs1WKv%8Ft*F`_~^9i$OX zF}ydTLXcEwtWyjZM#LH{NFyuOU?VEnIdU3{6+<&Tp+eBjG8!EfgHALQ;)FGj#!1DX z(+!0RLD~QsF%`qT5f#F>K^i+1!y^%K;vGn%sABkWM1`;lr12D|F-24ee*kG@RSbWQ z2-yH>WL31Y>V!D?3klU2i<7@1@)PPb+VT_Xgh)S3HSQw)BI4vekVas|@a~9M!v<+A z#u|1+g)j}IQCTs}h^P?egET%XhN~hfgd0E_sTIRb5pfb0NMklmPK${00n+HL819LP zHC>R#kHxSoBG#NKZ7!!OcS415NXi?Q^3F-hb>ceumRq$>tZl9RN#I&KtO**LqQg@(Dn<1vQseKd$WmIm3tuYH zC;Qi~BG2aht4rne7?wQ@}Q_W4lT22yRSR*tFL?8)1g|6jJRJ`kkJ)>o8O z75j>^s%KwORyFP`%BtG^xF=Qoe%zCq1wZad%|~zR&2?{h<8_I+<@X7**TI&VmHXFh z$<9g$5cGJaX??mxev}amz+0YJ~6j>U2YQH2TmZ4&*sP8-Gy@A z%&VurvGi2`I-8t|%X)rvol^aWu`xN{1!dR~PK?ReE+2u#N7oD(uZvhuuCO55>M>dx=I=IRd-Bd)+aEJb zW7O7gn_Z9FR)0si;WpL+bt~{JJ{zyrC0>q0ZOXHpO>LaIdUevi$sbP`Dc~PnU zW{d63E!QpzO$T>*sC2|b-=16YJCAQJ&0pG9{Z!kTPgQg-D>d9ay5TueZ&0sseY$?3 zC)WZIQmKA{1+<{lumE@Sb}db0)q82VdZm5cfck}P^$R;+C^anP_IW<4rc^M4w)*Cl zYnzoOyZ0JSsiC>8d<~I(y7fx_T3`c9?ZUSGRG?%=qwAlWH>qT5Vx@@${C)SowFw*A zA1iA6V+FWxo4>w4cArwK0&vz%gBI5KyV;dN=i{9pDOntz>K88Ivf<`*({HkR8f6IG zkIk=GvKD*94=rBTKXC`&l3BZ~HZ@UwUsy$;6b^gt0S}4zHgHwM-LE##Ib<`ZPQGyd zMHfwHmtqs`BZEBr$S(Wo6fj?GIT!RJGo1|b#g@xJzKru_kgmb23=e=#dW(8K&wRTtn4O&nqOdlXJ(CpDmuw3Ueh69w!TDPwh6_*h**{8 zg}b{fEx?lEJF}amFTv6A13dCcb-yfK(#_H&IHTQ?BBgp>8%r+iKDU{jIkbb0tX7uK zJd(xr&^`FA5`*V|nA(z@(wS(!2>KfI8t5jd(%|pVyPzE8*ZgfrKj?C31@tLsCG?k2 zntr81L$7zRaD2z!B4BNxMvhzj9gkZSW{ zI6NYzaL65x%g~6Jf=GF%q&#f|cYAYEp0;8*@79#}&6M|0%3GK6)~7srirr@r)Q5^Q z2&GdtZDuY(BM)zX_D#_Npr7z%mEjPOYn3O5Li<6}Q_&5d&mOEM%r698#J}mR2B;3r zA4)yNPQ0Fs4Y`<#4Y?N5QPo}Jc#ZkVm_8qwQN>vPS91Cz7jsw~d+Cy|!O*(owIsts zBYDep$+P|O^&HDVf4r?c7|W%0{cZXEj%^N>>Gj}&H)=ma$un9_PO%Jl_-!_WA8yanCnZYc&{5{I=a;p$`hBkeyJ<|Uc#a3ORUhVhc10v z*CLg3V*}hgq0;PaSklJHdrRBK)A_!uZ5;TXw(-?zH_%mfvDongQn4%Kg&uN@Py0YMQP&AZ4)@J_tv&C3}31` zv~4Cie@5@RSR9uFADPgW&X4 z#BQjHIa}57UAZM)PBOQxB7Me5RkqUQ-DbC4q|Z2A9OG1PcFQ7t#z{?s;&7tbHC5U^ z<1i4rh*8T`Re|x}S@bVvP`dq(eH3HWj8^g`{W34*);dDK>uUe}XWRiNExoH;9;O(U z+mD1Wgz9EveA%uxa7QZ8*c&N(8NWNvE4Lofhwo#`<-Oc@&Gh!-`%u{3d>13@I}%zl zWo$}#dlQB#RITbj6PxEp41)*{;rTIH;*f63;;Ii9=JdY%|#`M?VV zPSIy{9;|Lk;DD&zW?bHTw7X<@^TXQfpIC=a-3GJelj&XfvxXpPMC?%)u`1#R!D?aj zEXiQ?RTKW)4Aw^_e3rfW&ykFwabB1O>UPr~dbfnX*{8B!zskz~m6eqP_-*dd!rZn$ zl{ndsk#lzZt(LpOcvpc27+MJOQTMk%&y!5G;YTTY9(0h6zkdtTTw|@B#;v*A+OQGc zo@N?ow%=~(9UvbEj{~`X?*k1nQ!RrZZs;?h9S!{u^bh>241WeqrK?gK-bw|~Wawm2 zvmq(qE;4i{=r%(?1T8i68t6VlZ>Lgy*pLqA@i3-U#LN ze>>2hELH`OkNXFM+`qSjJYBT0#EJ_hpR zfj$ND<9zN1-D;-a0lC|!LGJcNkh|R*sc;qBLqQ(r5g=EwJqF})o(Ouosn}iw^4M!p z^#>NlwIKhFkAPk>-lst?8u}v0^Y=eNot7Fefjl&Asa|WmKY~28*FYXdC13sDW?BUK zEQl8LeePo<=p{2f5%e2Fx^(W>hRy^1mmw`_t})aEdeKlb=vRgofmR!m;%|TATp1n& z^)vJ^Xq}lZ2Qj|H^a+rvg39m=XuY8opjC#X0{nMFsya3qssR1XkkkzRYN#6YnxPud zMnkorR}BpY{g0u&K(81Y0@`3`DClKF2ZH`$NXw>wHbnOJnCsC}>N@yXu;*w7h1uhG zYA7`=*?E@yTu(CpzV*{Qi3=S!TQ{O^D^KNV%SNKN$)3d9|K*4<+oQRj#gyuEb8|gP z%Q2XVa#3G=wrQ>>`3RflQNAQkl0W#~>{B~cv={$xRUYiFVb}8tdL?uS z^wZFx(62xbggy&B82WeUVbFKb%NP!w2xZ!{`FiLv(0ieypf5m=hi*rL)j@|tPlS$v zo&>!VdJ6PY&<5y}(D6_uFvFbYK{No9pnE~jg}wuN9`r`&`=P&qPKFL5nd#}<1+N!D zmFzR1I-21U=q1ou(CeX>Lbc^)4)kYGy4uY;yzdIAa^Whd>Mr%wd_GS7MxT!pDuhQd z{UxTw@MJ_jA14<+A1B1T9jWAQpNOasq@<|cT`~MRBGjiq>W88}6;UB<0;xyJ{vpy( zs1S-E^$m(a141EG^+4(+6vNvh;^IV*`U_l~7*QeUkVy3$is8733gIM>`Vhr%YD9%_ z7D&B`VmLRVLYNLxzoHmsMpOt=?o*GY7_N+{U`I`=k5ddc!V@ZlkATz@DF$s56XK{9 zkoqJXwGvSw+y|mS!Mi^q(ix=Q3F#bBA^ZfSK1tD(eTBH-6P|h}T<{qY7h!?aPbr3t z5fy@@P(7An*dd}q*cGI{OEK&bQ6Xp=LcN$`I5;BG52XGK=@(HUoD5RWrWhI`Dui=E z>f;o{`4MqJDoDK@E=Y|C^*xaKJ*e+RR0!&es|QpJ+T2q5nWVr^k?tnta&w^jI5%b9>0sbvV~-hOWYUT;{Lv z8!BFWj)R)J_?|za<2Lx5kTxh98<88Ssf^@z6|I#&oA2i(9Xr2c!S7tl^0rcGmD;|7!pNCw`)~hcuMhs%$2k3n8jn9@B-0ZR5EKq9!wwQ^k@3jioKJYudsG zpVxtQHF0YJ`QY;fkPk0=6Qkc?s>vG@JgCG-%WJZ$;X*YHP=2@|H?s3ne%!AfpKTGN zx%21En0(=ssUI-uNhXgHpPXjzib$nL9Y{sBooYi;Xcu8gJi2b6$u5K{dk=B%cEjZc zDkUvU&YpgSu63L;b!O95X`l2UzQpdp*%_M~qs`QodS?1bbmzK{_E%{k(WQZ=vhn9M zL{{n309ELxLr;L70d0UzgkB0oV481&o�a?Of>Rpzniz9eO@gX*?OKZ`2fr7XcwJ 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

    CJs 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

    A^GX=D5i3g`dZe@cW8v4I7ZkPE^PmjZo zs3-yR&j^MRTSn!RAEt zdCrt!N8B{_Jk2!i+f3O$-v?sxj&=HLy9CqOcIQIw4*3g6ZodI?8sx7ae*%f`|4nCU z{|@qD$lpWqQL$SfS-7`Dz7BZ@ByUsig5=|k^C9{QpF_R{xg83BCFI_as~`tJz5_WNvJrABKaTx39 zAa8`+8uA{=0X!}6i19

    +@bciN%jIG!Ky+9pZ|mjQ7^&kuN=6XDfq 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

    1X*L{*aO-Yqb(HhrF6Y&j^U!g1 z<3>z8(^)0!lwD+AY8diq3v}==K0YUVU+(DCfVroZrvN`9t&i8UDA%-*0bfaYuH}ADPXxSk50tIe+AG z{xB=&f0@@-@M;INU-vy~vvdB~Y&zkjaFbx^Us29G?-pvJa&~>oC)l8fk;i;r&j&7+ zS2!*gLQ5NN#i!Yj567raaD*&9Tm>#?Y2d}iVh3l(%>KuE8~%zk#YOnVt4SJwU`kKE z|02ve-v4|GZCV8Eywuc71I_ zb{YQLY%al$wpTX6ny3sPcR8=Yxx4_iABYt|0}>gPkAFc+J^c zUbOK?y*%@}7@K)qcWr+(Isl^hc*>D9FLg2ghvk%)3ei@*m2+OIFp+aB^BRHw9bod4 z`%m~$cFuS?C323BAg!XD`?;K3Dd$-Eum{Ua?Vue8pZA?GPB~L?sq>Hi&xLZMI7#H3OhR_+Q2Iow=j0iOQ8$1o~_TH-o05~@fzI5YJS~zM=s|MDV`}>cPU%$ z6X>Uzb8Bj-2x{XR4jI8eQ4Ntz1vL$EO)=emjyxipQtk@FYfQr-ZMn$Ibt1^ux(Xg}cTow7@wJr3rz@INg1j?tO!YUhD5JjFAO=e)gnU5;FtW}kD=>)AQ) zU^XYla^615d63I_`&2UL9nI@l%NM=dLAT>N=hYj(4jJF(rFz72-Z9GgY?t$nSvhw$ zuPcxj&-6F_KSpns%y}oX;b<3@M&~H!n_SMFEoVlIAyJLOOm z!Pas%4X+lKTt>qyioc|Z*YdZZ%}b%VTDq_H9Ndg1OYGZbvjP@jiG3?Np8oE5zLnzf z#B*k>ke52kdEH+8544YD&fUz0M-}E=809?7gi)PuD3A@)-?BJ{g3rF9E>6jH`{v}B8^3HhdY}*uXc8`<5`Yv z-SGFf{!gK$K;^Z;o@R3gDs2yJVcIP^=GWXY@0N-m^N-CYe$2Dt8aw76#5H!z2)&TA z^Rhm(KCWTGNTb7y1}VHs+}CwZyiL z(RE&GI{vd=F!hRZzRu;`E0sLv-sZ*j%igccYg-S>&Y53#$Vy;~Hxx7#ggP z&a~W#agF86E0afPHr5Il{zV##9M(FGfBkSY$xA(k{~fWtOULWa%s#JDv$+uGwIghp zN}}^R!kt%1Dt=zPP?&S(yk0Y}c95?GmZg$cU4?n=k6ckiLEdfs z(Z>+QW0FdKA5{HiGDt*p$z zmni301hS8LxywDMsUI>*Ib%1$zRrs_{mA7EbhAy;}Ge)+?H&Tcygn$2kZ5ATT%h;sgc%lUw; zoDVXui;ydidDTA`p@UDBbAPir9RI^(J}Aoh5ts8pSvl93*N4cp1OC4I_rEtWb`LO{ zJMe!S*f7;ZIj?a!*Q5$@M2LTEJbmA6aBWay<0&IP+m}T|xNPmqE7HW{pMz~*%rp;w zzq#kmE3(gYpxLy?|L{x?j*jQMwtvjJrg%K@oDVUt;mDOG_sH_j=xmc^c&ORLW5`3I zoPXeQJ|rvW!_2GR<$U2WQ)g%A%zD8r7XF9Fd{~t8?JnoTvU099FRr;9gWP|8>RsDr zUr|Sx4Oe{b1skT?DCaj_&b29)2I``mCCgQvma}Qt1RwP@?0yz|8fJ5yr(vaXy=at= zbsdce4XCkx-s-(3uCb`;3sh`mQ_LalZ=QyGzcH>U|Jk9oGI zVR7(oJ!8W&z0}k2y!h>=j19|~U!IFJHWJ)~qoXD>nllcHG|VPAHLkJyxUtYiHYGfX zE25m^<#QxF^HMirJL9_J%R2ulyL^r^n|M!qWK=%WT=^WCV)-yV>@-0TYM%5o@x32y zIXB=x;Cm{R9d)WnZ@Fg){8=VNPY{C#xypj!ev^Z59Ftl4nX3rFF>(J|NT zql^cq+%fag2&UoBa^^8xYhn#X8oLsBkkZ7D=Q!I}S8QYXSDp76u4AaYcw>m!#N&d@!HNkGhV%&7j2Z&cb$fL`FsDq<(AXxUvW!-dS@j4xEHZNd1uQR4`QO@6YIgd*v zbDm&cJz&i#*Z~x(;dMUx%lgSJT&FDZH3y1Fu+*bcT8DifsVF`Daf14K0+8 zabC3X_r-`M(%g_DY@ALh<&-Z-Kc+T_u#vM-8lgIoB`s~xS#fSlAUr^$`BYX<$dh*OclkP5+SwQWVwooj0m5JUX_Be3Z_t==g`N_#%x3WS(9fw%@Y=R4$uRQWM~_}NwL@L>vNV-bDqQMb^L`)l$- zhrF07SvREYNZh}%OI|`BI~$7)d4?u0j)*x^mWIfA5}#e*yv8A~HspGmyxJjuKvfS9d_0QYZgR~fucgmz z9QVib2Zr2NlRt3CA5t|)lNa9T(9R{Vqt88y{GlN))8r2w@_MRjLlp;)zGGJncF7+( zF|Rk|Q<}V8O^ zDbgJq+v#Ipk!>^a%+us;5sb&$QSYGY$?Nk@`q*cy9frI|lXp1eT~yUT%y{DcoMQw| zCw$@{5FPcGic(s>;Q7fhjS9H5UqqV^kdLrvcAkPlK- z5G)x-&7R$G+9e;NPh9-(=lP%^&(!3D4*9dEARl(fpBeHFP5#UwfBqEYBM$j4vGdVpjT&ve_ucqSQ(9nbfys~(QU#p8n)E;e!F zIYS@&+Uk1~&oeS$QNE9enfbF+J^2dg9DUA1?~muRhFnpT&pPBEsPfNNv0nk5r;oip z|6s^1H2DXI{3BISh(}87S3no&lM?^?G5=`D<2Cumh?v>!s)@m_p~lU+81^;1xqIp~+Vq@~>1qIp(Vl`By_8tjWJR z<4ee50VZ-%@>lYfhdnT=~9a_m={*B$aTLq4m?*BtTCVBw9r8m%-lEA5BVvw?f2i_f{$bMsoH}3%&F)7I`5!}$ z*5rR2^1oEg2TRtm8+&u-a>RYRlQ#6h1_P#0%(4td zMpU-Q{~2;#P5v(;COLvCKjx}SO3rY}ap+UtB1goL7D{3n~ai14y$Zu(Kq(hEJmFy}+)n@;#X)ZZFee7(+GvrO09M2&qpsEX~5_9ct zQ?Wsa8FNDV*!ztHhJ0C*6FB5VRP_Kg5|5z?oVgBotU9!A`Ix_U?WD-LzrpZYhax$t$f{G_zw&Gp>b;-%;(-TQb1Rp8M4EaS( zPUetPP*oA$67%g>NA`2c^2zSV2zzX#FytYcoWdceqRP+npQ{%yb;+seW1qWI8S-*X zPUVo(P&G)$ylCMsRb6sghn&Wc&uMZRhx`mxe$27o2~0iRIFJmZkl zQ{~6J@o-jbykm~%3=TQHA(z$U^bR>ARj;7dNIaqrm<~i0S7nQQwG;Fr{2Q|~|yH8Q{v1h?^CZ1n)JkKGX z@MjRRQI!s@NDlHxS0+E>wlq6^)>$phX2>NPK!%ddZ)qv{EiHUWmX?+lOG4XHMl8}* z%ExYL4%YQ5j-`Z4@96C-;m59%ZrP$-}x@#J&j zAvqL}v6y(2#f~RG>spOirLOZu-oz;y6^Hge1?VGV!jC7viD$TuCx0lOWgw7j;+bVz z7|&E=vEwQD6!E;^#8c43vscGc(20lSP&~$B;!zemowH~@IC+1Z-GVb z9alFNJDyUZcp4`jTF;HAv=dJ$6Hi2ApixRW@sJ#fC$RYOD2p9W8P+AcQ@P$rnIvH> zD}Pz~)VA_d#>7)w$5Y0SM^2ag3uYusJY4I#!)h!Xy&a6jj;GvH#8aL=_Dm^f;u)ah zDd)sPawr~SG4Uvi9Zv<;C3|eiPwA0G&bs|okv<)){8TXUEZ6Z=2*ooPERsz;J#7o) z!4j%{?070YMLdL;4i9$PEm+swOvZ$c?C~464K&dp6|LQ{n6GMuxmVlN&kYCRCl$LvCW`=x2lbbo@=2ZO+ zZ&`cKv^;jgjrk?|>_*Hozoaxb2onM_|$wWLtdxJtpc+C9 z68A`XVHF?g1&o~+)sCkPIH(Ba4OkN5XJCnjMT)ZcJDDzsovo0$%-v28+rr&fU)#bf zyUHO;aZpPei`3Dyx%yrVRhRwkXKmT8^6-(_G_72LqCqxxLM3^m4}Ri$@+JPz7Q0W| zkt4k(JI|F9y;xR~m+7+)|EE$Iubs*K+nU^tnMd1;7K4whg(OQh_^lK&O9G3uRCexx zMNSWLEvPJZgzaICjF^J|qu>+0?rd&%)ODcG2K=89K2qA72oLHA+nWfB;COfVlFSGT z!_wEb6p;=a9OIhqzbc8iBA`^~FS^d&j3^;UkXufx%&sA9c zb+w~u#Y5eSj%Zb6~f*&%nOO3om-(D4?&n)VMj z=5F*EfdBom(bbS&(&VlISsFG|+Je#KtnGvYwYt<`I1$Un$cE%(9RSLg&7i|mY z@)={n6YnNCawb+Bd+tczhrj(bsJ%i_M_z z{NRb#utv4B?yCMOee82w50kMAx~?8U##BY#YgFab#sA(rVo zg7=*4gCFCOUM`7Wyv??S{l3mvBeI~)j z-^mO#`H}bWN*T!f$oWEkTSbeUmtm2G!o$rU_-KoqK={|HjfJ0Wzo>n}JvfN93pNsu zK1sLqaU&c|pW8U@&z(Ug!f`snL5$FD`w*%yVL0E5ef@eYB*ma|%9L|u@j?CcUBFVO%IIung>VjgbD$zP@r zXUCYU-^dX8zq8v{xa3g|d88qi(&Uj2S49QX4)+K@YF z@@R)VHbm|>H{lYOJkBAHHRNfUJk}wPrwTW!81o00-x=zXC(y?p8{-Z6Q%xT4kS9_# z8f4kEHc8*?Qw5A^@{ZHew@o~4bv$o7EuBVH0hI`E?W4;_-5gG*PZn#wPc!7Pnmo-Rzf0AVSCSbH z`CUW)Qj_0x$TO*W@=7v`KK84nGYvUg2i4q}4taKn9Q#T#$05%)i1D zuYWzsB`>0no#%yyd`puTI^@MvJ$ZgDp-)SUI@wK2S!~FeJNgzm7uYKa$)WjWEM|Tw zi#@-VvaTnuB+KYy_s&uiPi-B~Qm3WMpQ5GjIW1jo$OANaImvcQNe;EtSWHWm#ct^e z*2S$3QuyfY9@pJIT}dAtFynBAiD$WvXNA+!_o;eLb&MDLyIxn($KLI{Z^#EU`F)4H znyQN+N8ypKd$Ou-%xmZ)yTn}Zk+Ry5qcwT8pF=kPUHaHNJ#2NLvUhr8Z40w1XDj2w zD}{l!g*#!~L<)RL;}`E0+fqg>9c&Bl+sK89A@fzLrXh<2EpsvOk?m>qkVQ_P6+#vX zTEYxe@>?O>QnUzuTd6JK9qb3psstQ~$Jm=WN4S|^OCNi*d|)!4suN@=A29RwIQ)>R zoH&Lj`nxP5@XUU<#yW@mp&`GZ$sao8^;8uAOJeSRx8(sh=8qimdPAdb6LN}y*MG-qh%|7 z(xRpQ*>j6&=^L87C61ggZ1Og$@`EMiaFhJ~T=I7MB(%ue40)R-Z*#~ysQL@k1bECG zz7r}VM&yvLBIYVsb3yqBt%H2IUvTX0g38S_5+6oA?9!M%ojPm}jL zxh`apMRTRG*q^=qg0)Y;aVaa0M9onV%5Nie?c#%fC^h6X%@VWU zkCG$jNhxDCdgI-een}sDCw$bjw2>wsb^84nRU;Fsv-F4x2{Fzk8hd5?iaw9x1>bLc z%#g=x@-aV$Wh6g?!6TXdF8S#hvPj2wF%~;x$5~f$9GB9v?t3f!?0DW$U6=Ssj5s@V z_qaQkfjXMD)9-Q3%;eQ3fhwpetSEl$4O08 zc04EPvkyK9o8?;*Pbay6K>1envh^wTQ&dUMNU3<~PO~7MrCKFE;ds8IN;Zj%RBqsR`eY#sM{oat*YV!9^ zOV3jEqN>N0)q=KI8De ztm;xO8FHasz9q`(dvQ$iGrP~h zLvDU<(5EH-_w#ez)d+Fj$i z2^?vu;@J7Q#oF!s1hvikIS?AUvTqthdw>=zn`Dm zCO@hB3m2a$a`JPRDnCEdW{(;as2Xb3T|Ym(b8?#;ogZc++O}|}oHrIc@fLw2`B5A@ zKYy@xJ3m3~(ON|x!FUA+@Q0@?c7E=W^W^;eNuL4u-_OrIlb=DFe9y_xUsT!o2~=CP z>Mtihj7P?g$&ay^{3wgGC;@IgNq!W^&d+_;Zs#Ycy{T5whlyDNixg$C^Yefl8SasI zT<$xyyE}d!(x))iL;t+|z~tweCO-)BBNd_iO_glHWd{=V?ypl_@;~&sh~xfV>u*EO z`3B4=f0NvPQ0M;PzwK_f;t@G@;Vo<3zeQg9-L30i`kcXWzph87u3@^aM@}mqhgxy? z%RlD0&`HI8>#>ZwSn* zv%UU4H|9wC*w^@R4EeDp$8pGUsgji#gTWgz<(IK8IUap3;)Y3TC+ z_4(w~hI~eoQiSpLmDbdS>fk9Q zW?kvXDXb29zYg4o(`C$zrKgXaS^ajUGj-*8Q%Tb~8I!+j92qeRO~7{2J6pU|N|&6G zK2zb7QAd!$kn3r329j~M;(IS;hefiSzxW=-wjoOqa5{%9#bGHPvXp?OfNkNsRX>4M z$b3%@?+DGdE&Tfz-Hpm#`7^NwPq>e6OJc}s)eX4Esn>1eI8E8G4qp+K6zp0H4*%xOgx=5Im(GAJ5?E^ zT9ii}a$E`GS)f(ogLtg0=AcUUEmDeZ_!3<&@kqI=RpJv?$=#vg?>vs{Cf{)9tHupe zYVirHa#4j7Hp}}>?mi!=;tf|;@d;-vH&tzLSZ3US1>Nf_mA#JTp^vXIxkRhWt<_TX7UyzVANd{oP#v_-EQ7AGG$N#QM?OP7?p?rU(5QLDrU z?XpzQQ{}I0SC(I^9;g~=mH0qqeUo#3qe5f7UtZ2sAyB=oRpJw_tAJ5SgyFhYYnAwf z>ndneQrF$)yI&0Ix}sI$qw@2gw&?s=cZ6ObrzRM(vpVuerXQ5X?!iLzIgjIh558b} z@Y#_tqr4CqB@tSqw=h+bc|6fYcDGVWK_&HxkBYFN=I975P7!kay9<-E_iyBD@Ge!Y z5+A>I^_NGAQYG_P@ugP(*(|LRpRlSJRWj-%tKn~?U#C^#6IK;v% zEHFy_E;_is5(#L*9wTdBE)#NG;xf)dsFq~vQk1UyV zpG&SzpCj<`&#=`DxxXe?bI3KQngpt>F(;e-b;u>xq)$qa{n1jxkU!Mq8VxoO|IpT>rmAcR9OpOeD~sWF1ap!UIf`68+8mh>lnyT>X3|m zCL+UMD5FLC4wm9#scu_jQ^~(_%i2RTt{&^kf#Y%Tm~iL0UT!?~=_BLW&tW|iPgxyL zJ;u|%eUJXVJL+4mUd%DpfSd*3NPqp@=h{@au7>onue%$Vx?1YG8tA&Z_2_N>$V$w* z8j&M;j)TWfHGjS0*43Ckk8s>?S0hu`ySlDMtjk`bnoxzU5=+m%qb|7Qru4Bt57NYt z4`^}|hun;+o#4tWxKQF#%x(;V(CYUheJ;YBjP>J_iH7{UCO0G59|?fKM~@ATmMyl0 zV`G+Wk>$lh*JxGy_FX%_F`#{avAU~hbH*dDAY+m9Rv)U5JKiy4eu+NY;p5*wY;IyM zJeF9mxgWFoOUm#inO9PB-LyJnDGbX7+rm$c?XWGpBI_Hn$i$GVBt^FSv;`xS4HKSt z=U;7|MvWW$ywj3CmC-nVw6rj-7_VE=A~NPUY(-T*P5xwH?jK!pYx>w@zLg;#(BxJQ zxeZm_L6+HcZO)8JF1amz+FCKUG2|5EAVX>6klRu92aXB(x1D+3bNlpV`qa0`?F_k{ zCbx6Q?WrmXs;quP)627%qoo6V>~|5gH{@BG+@9q29S8L327)RvGvCp)LT1Wu3qFZ< z>*_=wSt9(sW=B)kXS%MAtP8V=^Wr*KB+JMwB{g5PEv3bB##rnz)|qwXz%e}WzPovJ zksD7J`rO8Ge~fiD@%*FX>8#q`t!JM;ufvgrZMXK%UCBwPkA8C@!&Nml#PHmBWfq_C z?yDPB{cu=nuamDJE?#5y&MWkJpnZhY%|uvAle?)1J9q8TQyf@gM%X}ANmX>u=;G3rVK!@s^tvKfcR zVKo+x)f2X*jHuSw7QX7b%vkJf^kFI+cu#QVPEPY+%48}yO) zd-;3gfrflqlLtEFK~#OL$<0!foaT}T(`O&Z{(ZJVhWuEQ2RY;+RJ{SRlpeEcVVD3B z+NHcnAN#!-Lkv0RL<+qj4tXe5vdrPK(Ob3ta#5E&j6TCqp}%4eHRRfwJd|XN1n9BH z#zV-#=NW6-7TFYdGGvSocSN$kGZv1P0ooFNfB105vkb@N&f~G8DKV&GW@Ci%v0@%> zVqU6a9`3{}r)lUTwrk7PV@ewQ<2#U{@zbz>eyADRAso=2LP z@9UUH`Z1d`Bgtk(x(K?la8}AZRTg`Gjb>f_aSTtq{_EbuRE?R#G4zr5WyXV#l+h-h z`jdQ1RE+b*SgK_0m6_|!omt)`kE2gzi#*nlS8DQDw$zS!JXLcv`D9eqmM(b$eL8^b zU&oC%jJoSG9r92v}qt4=jJdf`Bbc@2Tp0Oah-@V3D&? zV0rm%HDUtGX;`Evi+`5Mj|n9CQLoF$l-msuGbTM)L>7*{AxmL&(N|#$X2yxI1?Q`i zVGDZxOxS|aaV~7Z2)Y`!6vK5v*n$=DM%aRwZ-p&|@z7%B7-$`$RY#!nUF;Wefp3EA$u7^7Fopege*m1c|K%8$X)^4Qd(kuA!L!5%ZDry zbES|)Vy+spNX#`t7Kyob$RaV<3t1%Qh9OH4Sek?^67!3;rIf_nB4m-6TZb$XbGwj5 zV(wsDipWcbNwI93-YFt48OAjh`;Pro&cT1ge@^B|c%*G^+d+t4iimIQ#f@b<`^H z39F`4C1-jm>&_-U7N~}4mH6%bx@lh5uLOEJmV!jPp1EW<+<$yj8_ zB6CMR-4JkOezgc$Btp6C9QeqX--kzFk(i}vfkk3|!?qNWFRJL0O8KC9C<$g@HsPmQ z)$LaUXEBGE4BU%msr7N2;M_C>Dk=2gv+J1FG+lDSkB#9`rmzxoW$k1@&f=wshmnrq1KYw}!&JfAB6ZrGWN z=N`D^1@xJYUP+_^^yVA#cbYukAupurJt$-qXm&hD6_>n-K5aqv=lnuLPB=?dwos9+ zJ06Rv%A{kdlBzxu6G#76Q>(-$oWmtlrPqGy8=ORCU*|5R4^m*_Sz_W@t;tJNJXY^4 zqe`wKgjcrpVXUPw$;%z`GDH4Wlb1Q<_o%9`$-_S!8q0mZ74(U+a`>JhKhos)9P&!4 zGJq_l(7;o8kw0e4@6+cIj{Ci{(vZu|rqEmIkXKQaLml?E)SvKyOJ41eR~d2-Ox zW%2i`JJ4y;F78)L%G$dd=N-0G_Nygjecl_g$QphiWRbP(kZma;=Yqo_i>$q0hAgFE zIUcge8vaelQUaD!Axm*sz7JU>=5ry7#QdXeDJh%+A&b6 zR4>bKmFCI+o+m!xGwerH;bhG6p!47rf$FhViBGV*=xZ`>*Zwg%{tCMI^RHhHIHl$* zy7-X8*EnP^W_qxw^o3j<1{Ucrx!(|2BtP;xKwyy^$}6scMe;14A`L8(!|EZ6os{(K-TNe7OI4b&8s&v&Cz^;D$YXe24UR-72HsIz#`cXEGJ;e8?uyItEt5qxD^n^uLt-x{;7AeZ&uUcOrZITVHS|wzRZ9u;>KfF&OBVkj>B4c%H z$Rgu;XUHOBeox3Et=t!~a2$p#GMZ$q3u>1+_=RmLDVFLXi>S&Qi@gGDWnZ9cRr%>Tob=F1_AMEGx9E6@DY^P-SN zvbr5q(-%b~^Rm+kEYdsjK9Rs8D}Y=(7zl4LeALgJP4!MemLJ`QH(OsJ&LhtK|mC8x32 zE7A^fWbR2>IAX=WYHZjm(oXu=ySN=@-DtUtLT^W~ZYYDdD@0x}vl-HaZVc^~cRS== zhP*(NcRA!eRAtlkR9{!Jx=a4lA@4EdbDF%zA@8NC8vYmhz?Cn3;*$5#=Tuz0HmW1o zYsd+gE7RV9tXI+fRLNYIRsB-mrHupCK&{#j72lJ?QI``6#v`j%Q5ma6Llzm&l|vR8 zhca*im5eC)Op>zLE6D-)1>bYq?5_`hS1lF9JspqugjbS-RLN@>QfAM|J0?&ye@|J( zC#*U|mCR8oNmG>@9H^#gmG~e~lmaW%Ipkgn}GUk1U;UMwwY*k!Vt?8(f5lX)om#obUSwuD`WRX#|z_t{V zeqS82$kk1W2}70B5QbYW3k827i?*39G9~8#`-V({=>dTw^V$>BjG4j za-A!s`K?Fo0#&V5$|^oQ6G<|>R3<-m?O&3UQVG0-TbJGsIHNR2e8`c8dUx=f$ulR4 zj0(M#qWGp5uUmJ3J zP5wHPWb5wJH&kI8&a&i(F5fG%{JKl4#7D0!%FX+hstDAD6npVrEPBd~=Olfit$4mQ z@%*mI->P`5yHBU6ssyUEZ|4iu-*w5~(Z|l=DMPOQfeP`ILq1JacO1qOul0i$Hn`;P z=_6Nke(#(%2)xpTyn_KRCU5JnQ>?G7fkQIYJY`3_WKW`4Y`RXM=P?G z!(XUM3M!uHB6Uca43+em_((hOZdT2at7iXvsy@T=D>-T5qddjif3{Dn#0M(tQ%F~- z$^l;tIB&~GyAX)4`bn$ACtTNWRLMD4RN+s)cA@Y?zQ!l#YKOFL1Lr(RP3hJ&yzDJepnDFHPe7pOf z^g)#-=6i;SnjF_5#}AR0*Ir!DjX8lsj&H~ZH95XRPDoW#q&7KX znsjp3a+jQlKG)%s3_em48uArQPUw&mQ-we|2luq?ciJT8+}J6f7T@@qfbu698Y1qq=sBplao5+QiVax68qmLdWJrB52iKbXiZM*kke6>O_N8?_+_jcb9#rI&XCh=RMK<~IRjNW zG`Z%MA23W}_FzVboWYPwXmSRJoQW#Qb38n5Pb-U?eKE0_@*nGCs=CTDWUS*UvQ zxjU;9a~4CMq{&$v^0OiG*+vJuyD>jUpJ?4KN!+uByi=2(b;wavRfo5Xx{4KAUU13T z=<^82C1X;e4Ec9Wj&jJ^sj32RA>Y2ZVYW-oK_7e6WjEyHpGZxfm)#-fq$)3tNx$uC zFziQ{oQpoUaomqNry*C?)V?+*y-zJLJ4nmC`XcE4wF` z8*@G<=DdbHQIqpJ&f_G}I** zq)!)gL<$vvSHO_J)8qmq%N;6lX^e-Ed0$j6j=kEprMOrs+m;eyDQ#PL-91TL!tZH+ zf$==qxUvRQfm?{Q?nl~(JgQUbcyaxsTo)R5a~a#1Jd;#9o^s_>fLsk_D{m!OZm))hD8v6@`m zA(y18h9<|0XgbR!m!gk72TL0AdQC3rkV{jQA7m+o2MzhuC6{qxE^WxaYjSCaTsA}= zvM4!D0x|Pk&LNjI?L|PyM>cLOmYSKOoCainWdCB9ddQ5robCdyiQ-Xo9U8k&}V=Zb9F=h zR+FncCJYjSOeT$id&nq0bAsirQuou0yU*6<)$&%+rsQ!@Z-JF*k6? z^$mHGCf9e!4MXH2$%kil$&DOxLqq;ilN&nZ##B9zm}S&8*mWnCE6pbKv3D|!4LQ?R z$WR(P6-jv zK-RBU%3brw2-!d5iTB}8#nS|;&$a3$sQ7xNY)ibtsF?A{eQdsJCgagq_^MeJV-ZEl zZR*t=#j#%lYQfqIz(@K*Ue66`Z>ClB5sWt%7AeYNpBY<{lUE(~YS*Z6QW5Pt9c) z{HYX=WWFjbmJYV1gjiY{i`_dNSXVO~lPEGLT?&58cskOjy45=!Ogz8pcse*O?L<|1 zP-V@$e5Gn@m)w~?NiA|GLvFbPGL%jZxeHbPh*H-MF=OsZADM{p6vpdf$Rjnmi$m^4 zRTG>(azRzT%$qgbm|vmK2KX@b@atyCYc;tW$?{rKX&jgRh>*Ek<|jntUQ1vp37@%0 zQeY_s%ShW&tT-(4jmXBQnD}fBS){HdA&V>l6KqTI!m#uXS&G2&Qph4({ED`vgs5gC zm!@{UKXZ7C4Ysez4xPgc9DJc=kNk6a%2NyDyML04G zj87@4eN;SUQ7fcGc*BxZ*%+S(0HFAw_(`R}-&Mg&ITK}#)FU#gO7 zRrve;_G*>*gjM~h%A!?6r);?v)b*2AiBCAIuT$k`HTHMY^{0=$yLjESJ^wCLi1NCV z)d5uH#4%}m?5`yaq>sHK4KU<3nmoWEzd==Mj5@jBJ1AwZ!Kw~>Bn+ZYeN^q=6Mn;x z-_ztb0KT}M8p4(5bH^s@%+a^Lh@n&VZyf9Gp(W+8qhg=`+}B=`_>QtC~E`Y3aKrLOk)7bj~y} zP?bF>(cp8crlm_@k(Mfp-O?F`g8#nPke{hA^y-7yQMS9@%!{xvX=SX zmd>J&-KR56OLuAVOsA!@sq)9X^l6|<@|lWfwrOcESfr)OVz+dT;rJ~LIA1^|Eu{~U zxeqI{ve+%1OU`8UX&SgpsI;QD+tPXTk!xyycF#2}9jD225wl)J_3WN+BE%DK##i;G z2dZ;gHJ>fD&w2}}5`~m04fDLE;*sB~elGvxJ(@U-x~z46VMx^>*-KLbRa>nRAITLy z-wcanRo8B<35&?F*MuPEGf+uZ=_44g+!6lJ7CWnp$@v+Fu?h8BEGl|a)oq_Em(ZuQ zmDR;2t4lR`v6I!MCPF;%zAM?JU!c0KRZE#wiLKrjs-?2n@F5{r~oCO-|1`Iaad^VWOp*O&BC_BXrJ{|VbjbqdEzeoF0tBk@FV)P#pw0P>kYZPCa-tM zA5$f}O1TDGJFsL8m%M>Kr$F}m^kYNbugM>i?B74!4wjJPhgPuX3*#E0+jFDk!bfW^Z3gd9K5pG~~cIN+4m9PuGXF8aMuv2r$%vxWU-&%td}6#x}ac#aEHe`wV< zsKPtg?Ns@Fk>%)^MnT(~;Vyv`@d=;fc2Jd99roT$b-78P8mCp_qejPGSY+hs+O3(p zlN^azJZ0Y-#GLUPHFN31ng+lkMOo~u%6y89xQ}CUjvLXS`tOQpXLUDy4(ZGbXP3$9 z2~FO`tfEgjT3XOY-@z*-ie|QjBLV3GNBhW)H~xKdW8uB`TDDISLCgGN%p5Ip7fZ&E zve@m~!Ixx$XLtK7DbnlTjySk7<|pt#65v9<)~My;QZsVF5Rt4V(-58!5SHn9ml0yTG$>zs8NVJI67wW36JOfROM76FXzB( zs{+*rS|vVV)d8w9YgNI^HP;5JOIjs9TsJs2?J(=K*}0X!0pTE_ha% zP6cFr#d?~mln7JSmn6>(NFAtVLiGb8Jq=YP0ZGrPzd0UQ#>28csr3!e8UVht>ZVZlGkQVHW07q*sV zJ1iSQmLeBbt^>{ouuKa1WcwNS%tMy>uyhMqQeDE#4Ou3`QaxmexU9~pL0v;&$sY2# z1B+$<8zDa>sq0pz{9rgF@Wq)1GpS2M>8(q%wv*w6Tcr`vlRbuUz zp#O^h0#(K<$}B!yjf>0g3zElH`&n}EA4`r6m;VVkWi&^8!nL2H%CG(CTO+Ve`Y|`t zD)9-cexM4IfaT*kBPRx`{#qqIVbyu6{IlNn)Z-Tgszq8QK4I05R3+7Q zdod~fUfftU;1tvx@lhPvClrd6^D{X(#PZ@d3myiX+L|LiiX($#Uc%VoxkQfNu80zk za|E1MG)H{G*|iMe*CO*thi4rix#ktbW&mD67{KRj2@Ls^FsX5}KIHkZDfrsMA z6&XMLk7*?q_3aiipbHp_MA8MEyXH)7@5Ar1_GtKI#N%ME3#Wn|$Z1_WeFWpx`AvPB zjk4JL@IT0rcPYz?+^xyHNvak0zV04<`l9pw8TE(RBkciM${%r~B+u40?4MN0-2*)F z`se*~K%mP0yJ)cIfGT{)<1ebxs>9x>cTP_ZvN~U@#7B+9Yp}@qM{}%u7xzu=;wkqo zg4)|&Q}-_D!zP(8DQ#Stq?7@U+=jS1P>^njaOn$0d7cRb2#>vm$ zCPF;nUF$%#K&$>{encPdhH9y@*dyT|!|_`ha9)KfA=LB{jCTSSDavBE^bt9cm>A7) zJpO03zHnRmFMaC62U{VQN2aBvZu*v}7$e~^Rb6p70Ui}ow0tX2jex2ctdC7gzlSAz z$Wr*0YJ1=_85SwZBD*+DeeZfad~=7?#dqA5f#sfUDIsg-sgOl#K4vWTRpft+IVX-w z$=vfZxJrJdT++3RPk3#S3)RSo7nGYf@vEo?fhx;wZH7-+6+u-seYolH)0k6!UCp&h ze8Q?YRHfIdm*Vd06R4(XmH32J^3l@B2wzpY-*hz5uWO%HiBDK1og5jFOxLykubsHY z@l|)UN_@hqcvMAc)wTL-qXSjCJIX9RiqsUAS}>|UwMTe-a;oDPo_G^}+lpS6XHwpW zDhfa8BL?rGCM%0Q!V{2_m0q~IS@c-9-JZ({=_Bt=^=n8VA+j{Qt9~Yk7bPvVu3-~V zRSXTu1(&pcC&-{$Ws?)r=M;SW>?bnhv6`HSWQ=EiXIeC(5OVmcBndhAkELGci~IZx zqD`73K0NQpkoUfbm6Md5)TH39?eK}1_I{r})Ew~%_h~Y!{OgM^-^`dYQ04qX!Ne!5 zN=}u`ekn6975*SlRnsc*Va%n)=k-`)P7$iT$&5{#0!|0b5g&4Rcei=0oRsAFZ6EjM zr+Oqv&7(9&e8R1eZ&r$o@Y}w5%HLR8{fvF6RpO&+m)*O(7slF{d6 z`BHPlCtQ0Ps{AwX;x1oWYs&+z5+7B&B=KuJ+}hKU`MLyn(~QKz$7SH~qaM|{GqNJmvF)`D-|{5}QlMff>vu2td_R;8yZkycgxZd3O_ zHA<_*N5w3A+WIDD%%)Mq|adN zoBdwQVCM2t9Z!Z}mC$#MGEvnKRLTDH4JOq6~S#qmFq9@<$wN1uI|MgF2vl4}WufKOG2@Y4w_GlJ z2@s00lopF*&REKbCE8fzx59s`YfDA8=U`FBBXe1LN9JG<&tRxzgwh8;@g3W+NKqDh z4i+P40*=W#*6Gzx7P}*}IDPDIfhcA&mIN7=QY^@rGI%AZl4c4y%ZLK+xa5-bv3Hdv z47s=_mvG3XsFGez0rK9x^N+Z_T$(<`tzIr=$n7+_R6y3fT!t#Ymmgob+dNRs)v7X3 zg?pLtgnHRnOfMUY>1Ab+WXT*~&;mupPCW<*P%F`zcI?JCI&7JJlHBj-43%?!Vq3vTpwGgh5GJ&}Czky6cM zthRhbB`s0@`Bcx-8dUkCuKa@1ZIsF%KhHuXK5Ep>1?M?hWadhFpvg7-tTJQ!P#t5I ztnw=9fNf#sKeH_ynV;Jh`4d>4^iIH$ktd(RR~D(gRh$TpRaLv4`I>B(JywHkY=%n4 zDt!dwrHlj?MOp03*CHo9kKztz=XK59wYN5XWMbn9OD&W6UYcAh7^~W+4pkZSZ#i>5 zI^p(dUHU}B$0ye@v46`- zL;76BaXevZV93u%g(wXiawDpC;+S0TMc&DP>kjd?uL2s=r#99E|LU%hAs5x;Mgdum z@FrB{04pvYldI+(7N{m@RTHSxJw3Sa-C2xBMlV;pl?Mw?yk$x9 ze63U%8{CUV!74s#g-nVUIwa;+%ov_{edVPO6lL*8)Dc)1v&rFZ+rk`fHWobbPJxpR zKNZK$VQWTcuXRDYiehsoYaM+A$QQ)O2&$mOl1c*T&@V zjwZJWawrv{w4-Vyo=KK=x6AOEyNbR{A9+8Ve+R0aA(u(4Vs00Z_3Um>l|Q>1zcRO0 zpcDavB!up>EdsExF{4Aeee_SfP+Pfve+%{MUL$L zg`>WSMV{HKXm3>)5O~7U%e3^OCiein)(N?nhNY)FY9m zOq20tw|8ErPXUYE&yYuJazBUMpQ?JG;)!?p?Y`Lf#LUJ3`jofG{SEoJCif>92rq-& zu4jn?$$m9(pyKGG$!4XStLhR%8LbkZu<8w}#%R^}*A8JQNrY0aX_fed@1YH%Dkprz zH}=o152la(J>-K-yH=)Atr+BFY=}}x^(YyCs}{>WkT)Ij5JSGB$wM6SP^$d(`F!eq z%T)CC$Q-78tiBj($mQgw3(8Q3Je;a3I4rx$I>X!GMU9xfJc2$)VD_)*ha2)FO&%VQ z^|@&zRWdU1#A~>*#Na@6Myp0b#hoXwfMgso9&_C!vmvm^YYKUd1;2P@pHZ`0aqKl= z6l?c0Cik_1+UIB$eFWqE0gDu6vDbvrSR|vHpE2~A1cZM^8*TE_B%LCU zW`6wr&q}aJmX>lyJBXpfeO zJn{DbHQ{#9uJ@sm{V#p+6YpYyMRt{erG7?LS72EJOHSis_s%5NRRPD4e6Q#B zQ^(!jnM|MJfJ!`4CYj#3qwAU!^p0}z-l8gl{!M)cd(62!g+9^n@yTx)@(Y<%E8cR* zQ$yq|`Cp&!#{4#Yp!Q+%3}A<0&?Di z*Z;qoSV*6qR!bL{maf#~1x`yBIV}xTe`(bs)6&*as-?3c6X8gTk+6@A#!qp(O(7Q3bIk<$>z@We~mbxJ39&0ImB+CYepl=n(I^=a!Wztzs zxAC?AT=IJQ+=dUHu&guWgPOd~A%8@b>}RE{8eI%8KuTZQ`}vRQBO7I({E;DF(d3UD z@&>B>weW|jHMY3qjr76I4ny8x$jNf4Ja2HwpHL+`;kbBo{;6Lxm%NESw}J5AW$}q2 zm(=7>9P(zWyp^gakBx2g>0phGt%kfuleaqL z?NmKE8$0N8-O9#xL(Y|3<$1e9-bvMd9ZzJ5MBChK?4pm{%k}fT(~#fRS@@|LxX^7n8_K}S)d9OqM)Q~ghQQ7#^A@8H=$=TRXA3GcS z47r9T?{mlpsPeCV+WhtJK{w`uPRs`kdA%kdaL9+KYNy9(s-@9b_F~TN&*)>X{D%zr ztR^3F$cL$t8@q`?-}}jdJudlk`do#N-_wT;IeuQ1=ffo9+LQa`6~L|Nn3@05iRY+^XRD6qs1py#p?Cs|ACI!w@f>4aFCbRQPnn@@W4Sx` zl@re~6Hn@VkfIz5#Zv%Al1)5vo)0V%kK|BU?0Al|t^+uh3O;*|r;Y38=WF`xL)yeg z%5f7nWJIkq7CW93tg9@J$yiO6@eFE>*Cl-t52oBoo~8(5^jWX36r9nUG&bq>d5-%=_6zjNGpzN60x zE1pv(o_;!>Qzjm{WIcli$&5$NyT?Knd13F6vDopPW?kQ+E*Tpqhh&F1W`4e>&pw#_ z@pIb5b4$5A?Ce&p8v% z2p!KkKR;ZbWp&a*HpXg(s%`cdE3dux9xxjdA@2&cEEf%Vt|nh}$d{gie3?En&wcVGLq4daSGARjKhVNA0?x;t|6l ztr8!Z@pwfD7KvMPtj`8sCC9(klY8$$?d5SY6cv30vXMjb%W5`*hDJXXw@*h++z%hwu$wvpTy5xKGv9s}qA@|kf zKOFL(RAm5Fau79k+ea?>FZ#%8CACZW(~u8o@}Ca*K2^=(9T$%yxhljG^8@<4Xl3KR zA^)z)_Z{*>sxZ_@KJi;(EXpx^@NfFac=oUC9vX7#A}Y@h9r8a^MS=YP$MYll*yH&h zL!O|?|2X7-slr=SS*~_0bIFbQF@0pmi6<=o8uA8B{?{S@_Y`CqkdYBK`9DK`tjYg5 zi_@%`xQ7gc$Vh#!S~az)bXs(gAk`0FIK%N6et zsHSUGBviq>>16aV9ywR?{bLtxi=51Pu2eqq+b`hA3aL2uoyoYY-CkFN+DjHwD*6b< zn+S^(WwF=Qc;w{eQGD+j{|;TuaTuRI_S}hQ@^e;`;|2NAtOQiayJn?aX#4$d?r2Hq zkP{g4LrqTLkP}gr6I6*b_E+r_)8_$>`#qV+kTVrm*+}G&lTd}_gEKDfg^oym%xom3 z&l(_bqmCtsA(zwSBqXDkWjp4r!GmP!ciBREi);(u6+FvW?A}Sny3XQQQapCVub9V; zCpmrOQwjd)O=jZhrsGK#v{aAY6jb@Ug6gxojtErqwJHTv!RS4K2pEqUy@zZINAGT9 z!4vNSI5K(_#~!`%eYBAg_UH|2&ssvM=pz`f9V}9m#m-MEa-QK)oKyIB17r4AYWiG- zk3SMpnfxr#Gf~BuWr2LWRpJAcwPVaoRZ95yzQMD3S|vW= zy0TE^N0{fY(RBjVHmwq$a9vrcN{hPU;?e5bjfR2hSFIACu}3D=d4D!fUHb!AOe`%+NX4y_WO za9!C$bzND0t$Lt3tySU^t}6#sQM#@UM_Zf>R9WQq3ySzaWql$wCslG1lpSo+n`zf6 zmA!+_MV}+u2S2==W~`RgFsxQRSBVsI?8q|(ZLcNopb@}fQII`-ke9GJIodWccbAX@c{HAvb z>bmkfYg9q1a_Y9ne(rvOKHDvFK|^k+$psy9A*!-z)r`xZ;*=^av|}zzpJ-V9b+wQo zzoyBBNQR5I!pa!>!Wz9r$jK{CD6xM+wkUmMVEAI$7N0516mAstlFY!^fJlYb> z+#X)eee_S zT$h`)D9U25^Hs@-eLSzwD)CYAe5x%$UtF*(JtB@ju`jBzF8>^tWm>OT?f_S(&m+X@ zuM*WvUp&@zRbyXBd!DVJ+NI*LSEL%`q!uTX%eAU~<@S3``dq|uzpfgluJW2(Bj|Sl zq12-4$>)OF^f?4?e}vaE9PHL6- z;4EzI**a0>-*GK}DsFW*p3d}<-btX~UMCYzRv8W`ot&0-p(+Z;{(tXurH|b^T@1Om zCUD zdQ;`^ZRB2Vpqi*vy`c*3hFvE|&Uf~`Tt+Asb1zp}q}Gez$Sy^3>^mNPSi8Nq32HA^ zRjKGB7;hphQk2CW;eE+@^4-FI^r>#;r?1J+c}?!?H*!dYij(?9R_Pd1x>0{5P z0VY4sR#z<@5afq7;%}Ex_2j#SgXj|%2!EcwVaVk*`3;9Wn5zA#NA4GV@#@}H?mQhr zA6b0-{rq42ncQKZnCiOK)iMu%M;zlQM#;v3Mq` z9VseeyB(7}l0LGR@W~?#IZ;g&^9YALimHO33h&wUGmwUuw z$^Gu%F65HO(kBCgO8`G9V+^^gCXaE*Xf2gh&x>OH_^t<7WQ-{vdyGva*&bs7d68DphvZ*1 zSy}8cHi?|<^upidYT9}}0YV5N2?PlLneXn-?%uw4I@xFQ`M(dy z(!81P&dly?-+d>7$DMmWL2RGM+kxmg!P>5|PJ+}Q>96=WJsCWs$=SxMCkgV8H1bIn zS$p*qNV#6MQV(jWQ^c!dQp$&l;`;DZ;J7}tIQ?1*Jh13KqVS_At`C0#oZQiL8hCj4 zYNF{UV(I3*MT?%FSWEFYor;1~?lOM{c(x>n)kMmqv7bghJwQGaQn}0gPr>sJA)DMe zQ*^#iBcExJb?%%6DK~el)GJ!*ERs@X{`>VRcNE3VowI@C=8nZVR7-&eRzE-#eiX&c zopXSbyM&*s(jo}sxpTzQjT;p5Io497L?@mgMbirK&*V!|G(nqPZ~P2A*E`7P337)< zJ}*E%A5s$tnY|Rfo8^K)=ko>mE{%MCfP5jO9wyYa=>PBTc}hgQ|2)w7LP6f7QF-IS z0Qn+FQKuQZ`+oO}&5jSfaWQxX9dBGD$h{i*q5%04Nd1D4>6gT%XAI5>kuL?0J5ISo zknh#VmjuX{L8^#QIo~>F4?GnjUk)C(&wrU9Z_=c^aan+T1*GU`F-p{Fm-gNqBL4zB z3mk7;A;^bmt_rNU8d43ECYSL2cfR(d(28G!XIBULYC#^; zOk{el4v?>b)OGZZeo37D!ppNm6#lYj_i+b|D`2T+1;H&#B+s zKJD+o-(|Nqi&&`6o!QLt_ZU!=q zSa~yVg3s47GGo%MC&aT5lriLCM2c?pZUypp=^cCitZD1iS>!V{@~wnSQqCOyZIGhs z3^PJ4v+jD%O1(r<-138m{-xV~r;SRCp?@uf_HUod${Tsgr z&l|*JO7HFBpRaXVw+EiT!$W@M)K9xY&;J2D?yUYDg1q^3WyKu<@|}>%ou_w!=QDyZ zd3vWHAF7e>4Dytb=|6gIAs)_C=JE6NZXmmPYLWk^k?&SgPM-b|Qf{7FshSxoPr-xK z&mhVe`pr^aBMLv3veQhJr;6g{={-oxs7MLgwe5!1MP)%EW2( zPTl*!GsQu^SCC)S$oB@w_d|-73E}@=R1EwoEG-X!=Wqx4enB2LOX+-nfczk&c&{4$ zlGyX0*~t+3A@JNp5UUXnKMxA>u^Rco0Qq4^b+M1BH1ZrlC1AfK+09}ke9 zgp_GvA6j?r=OOY_;5ot3`AI>(LnA*KAU_SM9ke&1Ti9p7GsZ!FT9DV7qrCBSfcz|^ zOnHuOcb)^!Mh^0`g1o;*el|dU9#V&q&OHDA=B}%s6?)?Z@bDDUYQ)3O^MZVWMt(j( zei2esnF4vvkAHqZi2M?GiX7w@1^H=>{9=IoavnPW89Y3kHafp7$eZj#WO`l>kYC9| z=U2fq#X){WkmqUSR|4eMAeB1@UkA@vLN;~XYl3{0Mt&_oegjgwl06)G7q0zeedy;m z!Ly!&{DvTZsgd6Zkl%vTOpSa_@APDd{5E*p=zdF(>-SZDek(wJ2U5SF*WB8l^X>*G zgvfsZPZ1SMQwzK!$UoD_?*N$~67Ld)m)jVb{-bBF{pbc=k8%!CMuEa@B|m>7$_w-_ z+tpHvp=*_KPRaqQNct1(p^JD zY4<2I4p7LJ=USq$k1S>MfvU|_6t_KnAE`GGH(U3^>(|&W^y**1!?A42*!$wuHbUm- z{UE7X81?UE4&QqWv3q?r?PeNRZb_E1f?I zkUxf0?$Y~r@a*Xze=NucY2=Rs zwjorG9-e=a3R&!++MXOP+pZ)rCEyw+Djo&Ndm*8>p>%ZdFUD{RZ{|)l%D@g51sPXN^ z*Whu-J6{R%VvYP&fc!s5l@T&OZ!FsH+^{5k1D=&hIaB}qPmnLx$o~tFxe>SL3!>Y& zQQ%29$cfcV8h_HriPgrC)vlkv11Y0((;8C`4Ru}#Jfj`t?+Efo8u>c`^2(5UmOR00 zAfsvUNt}-4Jn$`fX*9U z^2rUM&Z`H=qXqd1jXXL)ULz0WH3Q@|1bMd(<&8B0&7%I*@81en#H)xG#$J91_UfAmMhL!>k!z7OFR2nLZ z8>brqXB_cR!ily`W<3~sY-8}eLGMl6ZzLYu`5=Y7k@Xny@v{k}t|ZuP>95T<`kEH} zI1P7BZ&UC*>Ey*Gf?TPQH?hb%FE)den-^BU+Z2*uTBOQglzobRVS{cB2+ zT;k>6R^V}ar(23XqkEJ-TdEv%=CS_|QmYacKcC&YSl5UASBsWn9=}c58d6^p53e2X zwBDXOhx&{G&!eQK@z2(xPnSmCTIs`k-gJrD22z}Z)Rjui7=Ps-t<hLkBSjc1LyBt+f?JgyIS7UX#v zd1s5POUw5l<(3vJ^@NuCp7^k7Q29_%Tp#W#IL3z-=K+%9(gGefCs8(}EL9ZOhvR@V zo_HAN#kO(!WSQ&3?}Nwn;W)AM4vjp{TFMF1vm2z05AWFg*Ad7?;JKHQHW9a*Ab+5d zceBXahvOmT`p`=4bBMBZy!h~CqHsA>6xW9n1jqQ$;>=v6de!){B$VX`8 zk^p%!q{b3I*Gp$lc{bwar77Uy`q>~)7UaKc8ZAeCF^J;5`@LEb}i;~+B4L-5 z5637!6@q+?My@bk9nW2mvxpbS=#Q~m&+#aeiSjd-g8QR*x3Wl0U_UPqitFbp=##r7 zRD-98XMcSu^lB}5 z7Er3D*IgqTER&BSfwP&j}PJ(}{cDQQ(;x80+lta=pId7(=wW}SOH3hR8ZM`4`@Jqqi*$fK~%hk6v&`3RRXi8VZG zRmWqK*owtMalbRdo?U5_xu4{Lao_B&N)!ns+KyIv!93JgcY64ZkXnnG=xIJ?A{816 zHat&iDdthM&xle(?^K?;?Ryh&sL52lq1prPv!(9XAF0&A1LTd5QFpm3iW_mwz{wpK zTw+!l^{5jU%@P+^5;8x{RKoSBM90P6kaFX~N=-ag#l_wvrQ+f#qHt-jl-}c1Tv*E5 zKUQ&JDZeHP$AzV&j#qJEDen=5A4PHF0=9c`VJRjqEXBlyrI@&|6cZPgV&cM5Ok7xs zi3>|HabYPYE`%a+VJRjqEXBlyrI@&|6cZPgV&Xz55*L7+KYKO0WqTiM`4}Q9))#o^C+xyheu(ZJ6#G&_<7XGi|r+>&*?&; zKN2^S$7<+bShBccd6snaivjX zsD?D9Znl)V8KKs+-6vZud`vK=M>IQ5}oUi}&vZuqkj(cD$jar$C$A&)q@yE?_Li#2j*U>A@*yDY`nWhurkMRD!o@fDR8 zT@GiTJMT1WSNR3XF6Qy=>dM2e?!c}tv1^e=?h5PzvS*j27`rUR*rh10T|7#(c1`%? zHEI$}jkLvu$}Z;d?dr|LuD-yoUa_lLBliY&0ok+5QjA@eV(d~B*DfCGTDx9JFWlbR z^{Gysd3?M2^RR0mu&ZC}+VSVgWBq|$K=$mi6l0gA7`qh3wTnmW)~?&8yzp~t*TXt> z=JD+s%EPWh0=tI9u5UE*P+%93J-aN$*kvikE=6(eTIAVvQuF8|tX-E~q`b;JzFmv+ zuxm+R*J82j1&zEounWkZU6x|(vJ_*NqPTYP?1{~<_BZ~}YwbGpVr3We_;wwZhh2vU zb{!^mU8j)`3+w{2XP2cIyDY`nr6{glJO^a$8g=}r7pz?~E>U(dk8jrx^RVm4z^)&P zT_-*|W=1j9r#u>{1lhuA@A=ZoTXII&0T%mnyrM$G7WfNbv}PpTU;K%FVx7#n_T7$ zOgsx7Ds`O8oPmi4T?!9V5_b#5_2G#~>r{HjIrOhHb{Y)zISD+Kjy@-fKCf$iPW1FS zfv|uq`W)_3pbsUad0c%?Mp`@4JMPan46Jc;sLv_j;k6%=rzeX(pKE7E< zE`-X@AJQE&6*B*|_2nwRn8z=@KY`Sa#1pU2Y2ew+(dQ?k&t)3qw-hac}4TkKZ&aZ`FM?dRe*doq<)~KqVL`PHF)08JoHcEYC*n5 zBVQdLUy}#&Zvy0N1o=aad`*CSEu@lKpI5i6UKslMI`D9>fPTSqtsrl86_M$=HbA}} zQajQ+j`zR6JME(o`3CT~e!gCi>oxNA0rHKI;@9gF@_VaneOQS6Tkup;7Mtk3QIHSQ z$TwPKy=rz7q_!he9-kkXnDK>`x=TylL{jKEjOWe-SESH~+ivWqRojRY757w-5O{=<_3RQE%{=CoWT#GNFy6_+cLQa3ZnQ)oN~8Y3R<2 z-VB_%#KUpQUq!ceb(7SdL&kg z_mDFA&bMh=9mkpBp&1Ih3$N#?DJ8T1=_!L3*C0Z%(+`sNBa@kc@4>RRQEKU!p+JAZb!n2hZ2T97{_s4-{4U=2>BL0;o*g~#G9l5Q�a62B(oTR`Ec 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 RW_T(-8$ZncJ6-Mk>wjEp$#4Jw literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/scripts/vs2010/x32/pugixml_sd.pdb b/LifeGraphics/pugixml/scripts/vs2010/x32/pugixml_sd.pdb new file mode 100644 index 0000000000000000000000000000000000000000..38170c9a30a43d70abbd9dbf77848a38a6fa6025 GIT binary patch literal 274432 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 C*km~X literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/src/pugiconfig.hpp b/LifeGraphics/pugixml/src/pugiconfig.hpp new file mode 100644 index 0000000..72b363a --- /dev/null +++ b/LifeGraphics/pugixml/src/pugiconfig.hpp @@ -0,0 +1,62 @@ +/** +* pugixml parser - version 1.0 +* -------------------------------------------------------- +* Copyright (C) 2006-2010, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) +* Report bugs and download new versions at http://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 disable XPath +// #define PUGIXML_NO_XPATH + +// Uncomment this to disable STL +// Note: you can't use XPath with PUGIXML_NO_STL +// #define PUGIXML_NO_STL + +// Uncomment this to disable exceptions +// Note: you can't use XPath with PUGIXML_NO_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 + +#endif + +/** +* Copyright (c) 2006-2010 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. +*/ \ No newline at end of file diff --git a/LifeGraphics/pugixml/src/pugixml.cpp b/LifeGraphics/pugixml/src/pugixml.cpp new file mode 100644 index 0000000..30098ed --- /dev/null +++ b/LifeGraphics/pugixml/src/pugixml.cpp @@ -0,0 +1,9574 @@ +/** +* pugixml parser - version 1.0 +* -------------------------------------------------------- +* Copyright (C) 2006-2010, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) +* Report bugs and download new versions at http://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) +*/ + +#include "pugixml.hpp" + +#include +#include +#include +#include +#include +#include + +#ifndef PUGIXML_NO_XPATH +# include +# include +#endif + +#ifndef PUGIXML_NO_STL +# include +# include +# include +#endif + +// For placement new +#include + +#ifdef _MSC_VER +# pragma warning(disable: 4127) // conditional expression is constant +# pragma warning(disable: 4324) // structure was padded due to __declspec(align()) +# pragma warning(disable: 4611) // interaction between '_setjmp' and C++ object destruction is non-portable +# pragma warning(disable: 4702) // unreachable code +# pragma warning(disable: 4996) // this function or variable may be unsafe +#endif + +#ifdef __INTEL_COMPILER +# pragma warning(disable: 177) // function was declared but never referenced +# pragma warning(disable: 1478 1786) // function was declared "deprecated" +#endif + +#ifdef __BORLANDC__ +# pragma warn -8008 // condition is always false +# pragma warn -8066 // unreachable code +#endif + +#ifdef __SNC__ +# pragma diag_suppress=178 // function was declared but never referenced +# pragma diag_suppress=237 // controlling expression is constant +#endif + +// uintptr_t +#if !defined(_MSC_VER) || _MSC_VER >= 1600 +# include +#else +# if _MSC_VER < 1300 +// No native uintptr_t in MSVC6 +typedef size_t uintptr_t; +# endif +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef __int32 int32_t; +#endif + +// Inlining controls +#if defined(_MSC_VER) && _MSC_VER >= 1300 +# define PUGIXML_NO_INLINE __declspec(noinline) +#elif defined(__GNUC__) +# define PUGIXML_NO_INLINE __attribute__((noinline)) +#else +# define PUGIXML_NO_INLINE +#endif + +// Simple static assertion +#define 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 DMC_VOLATILE volatile +#else +# define DMC_VOLATILE +#endif + +using namespace pugi; + +// Memory allocation +namespace +{ + void* default_allocate(size_t size) + { + return malloc(size); + } + + void default_deallocate(void* ptr) + { + free(ptr); + } + + allocation_function global_allocate = default_allocate; + deallocation_function global_deallocate = default_deallocate; +} + +// String utilities +namespace +{ + // Get string length + size_t strlength(const char_t* s) + { + assert(s); + +#ifdef PUGIXML_WCHAR_MODE + return wcslen(s); +#else + return strlen(s); +#endif + } + + // Compare two strings + 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) + 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; + } + +#ifdef PUGIXML_WCHAR_MODE + // Convert string to wide string, assuming all symbols are ASCII + void widen_ascii(wchar_t* dest, const char* source) + { + for (const char* i = source; *i; ++i) *dest++ = *i; + *dest = 0; + } +#endif +} + +#if !defined(PUGIXML_NO_STL) || !defined(PUGIXML_NO_XPATH) +// auto_ptr-like buffer holder for exception recovery +namespace +{ + struct buffer_holder + { + void* data; + void (*deleter)(void*); + + buffer_holder(void* data, void (*deleter)(void*)): data(data), deleter(deleter) + { + } + + ~buffer_holder() + { + if (data) deleter(data); + } + + void* release() + { + void* result = data; + data = 0; + return result; + } + }; +} +#endif + +namespace +{ + static const size_t xml_memory_page_size = 32768; + + static const uintptr_t xml_memory_page_alignment = 32; + static const uintptr_t xml_memory_page_pointer_mask = ~(xml_memory_page_alignment - 1); + static const uintptr_t xml_memory_page_name_allocated_mask = 16; + static const uintptr_t xml_memory_page_value_allocated_mask = 8; + static const uintptr_t xml_memory_page_type_mask = 7; + + struct xml_allocator; + + struct xml_memory_page + { + static xml_memory_page* construct(void* memory) + { + if (!memory) return 0; //$ redundant, left for performance + + xml_memory_page* result = static_cast(memory); + + result->allocator = 0; + result->memory = 0; + result->prev = 0; + result->next = 0; + result->busy_size = 0; + result->freed_size = 0; + + return result; + } + + xml_allocator* allocator; + + void* memory; + + xml_memory_page* prev; + xml_memory_page* next; + + size_t busy_size; + size_t freed_size; + + char data[1]; + }; + + 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) + { + } + + xml_memory_page* allocate_page(size_t data_size) + { + size_t size = offsetof(xml_memory_page, data) + data_size; + + // allocate block with some alignment, leaving memory for worst-case padding + void* memory = global_allocate(size + xml_memory_page_alignment); + if (!memory) return 0; + + // align upwards to page boundary + void* page_memory = reinterpret_cast((reinterpret_cast(memory) + (xml_memory_page_alignment - 1)) & ~(xml_memory_page_alignment - 1)); + + // prepare page structure + xml_memory_page* page = xml_memory_page::construct(page_memory); + + page->memory = memory; + page->allocator = _root->allocator; + + return page; + } + + static void deallocate_page(xml_memory_page* page) + { + global_deallocate(page->memory); + } + + void* allocate_memory_oob(size_t size, xml_memory_page*& out_page); + + void* allocate_memory(size_t size, xml_memory_page*& out_page) + { + if (_busy_size + size > xml_memory_page_size) return allocate_memory_oob(size, out_page); + + void* buf = _root->data + _busy_size; + + _busy_size += size; + + out_page = _root; + + return buf; + } + + void deallocate_memory(void* ptr, size_t size, xml_memory_page* page) + { + if (page == _root) page->busy_size = _busy_size; + + assert(ptr >= page->data && ptr < page->data + 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 = page->freed_size = 0; + _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) + { + // allocate memory for string and header block + size_t size = sizeof(xml_memory_string_header) + length * sizeof(char_t); + + // round size up to pointer alignment boundary + size_t full_size = (size + (sizeof(void*) - 1)) & ~(sizeof(void*) - 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) - page->data; + + assert(page_offset >= 0 && page_offset < (1 << 16)); + header->page_offset = static_cast(page_offset); + + // full_size == 0 for large strings that occupy the whole page + assert(full_size < (1 << 16) || (page->busy_size == full_size && page_offset == 0)); + header->full_size = static_cast(full_size < (1 << 16) ? full_size : 0); + + return reinterpret_cast(header + 1); + } + + void deallocate_string(char_t* string) + { + // get header + xml_memory_string_header* header = reinterpret_cast(string) - 1; + + // deallocate + size_t page_offset = offsetof(xml_memory_page, data) + header->page_offset; + xml_memory_page* page = reinterpret_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; + + deallocate_memory(header, full_size, page); + } + + xml_memory_page* _root; + size_t _busy_size; + }; + + PUGIXML_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); + 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; + } + + // allocate inside page + page->busy_size = size; + + out_page = page; + return page->data; + } +} + +namespace pugi +{ + /// A 'name=value' XML attribute structure. + struct xml_attribute_struct + { + /// Default ctor + xml_attribute_struct(xml_memory_page* page): header(reinterpret_cast(page)), name(0), value(0), prev_attribute_c(0), next_attribute(0) + { + } + + uintptr_t header; + + char_t* name; ///< Pointer to attribute name. + char_t* value; ///< Pointer to attribute value. + + xml_attribute_struct* prev_attribute_c; ///< Previous attribute (cyclic list) + xml_attribute_struct* next_attribute; ///< Next attribute + }; + + /// An XML document tree node. + struct xml_node_struct + { + /// Default ctor + /// \param type - node type + xml_node_struct(xml_memory_page* page, xml_node_type type): header(reinterpret_cast(page) | (type - 1)), parent(0), name(0), value(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0) + { + } + + uintptr_t header; + + xml_node_struct* parent; ///< Pointer to parent + + char_t* name; ///< Pointer to element name. + char_t* value; ///< Pointer to any associated string data. + + xml_node_struct* first_child; ///< First child + + xml_node_struct* prev_sibling_c; ///< Left brother (cyclic list) + xml_node_struct* next_sibling; ///< Right brother + + xml_attribute_struct* first_attribute; ///< First attribute + }; +} + +namespace +{ + 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) + { + } + + const char_t* buffer; + }; + + static inline xml_allocator& get_allocator(const xml_node_struct* node) + { + assert(node); + + return *reinterpret_cast(node->header & xml_memory_page_pointer_mask)->allocator; + } +} + +// Low-level DOM operations +namespace +{ + inline xml_attribute_struct* allocate_attribute(xml_allocator& alloc) + { + xml_memory_page* page; + void* memory = alloc.allocate_memory(sizeof(xml_attribute_struct), page); + + 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_memory(sizeof(xml_node_struct), page); + + return new (memory) xml_node_struct(page, type); + } + + inline void destroy_attribute(xml_attribute_struct* a, xml_allocator& alloc) + { + uintptr_t header = a->header; + + if (header & xml_memory_page_name_allocated_mask) alloc.deallocate_string(a->name); + if (header & xml_memory_page_value_allocated_mask) alloc.deallocate_string(a->value); + + alloc.deallocate_memory(a, sizeof(xml_attribute_struct), reinterpret_cast(header & xml_memory_page_pointer_mask)); + } + + inline void destroy_node(xml_node_struct* n, xml_allocator& alloc) + { + uintptr_t header = n->header; + + if (header & xml_memory_page_name_allocated_mask) alloc.deallocate_string(n->name); + if (header & 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), reinterpret_cast(header & xml_memory_page_pointer_mask)); + } + + PUGIXML_NO_INLINE xml_node_struct* append_node(xml_node_struct* node, xml_allocator& alloc, xml_node_type type = node_element) + { + xml_node_struct* child = allocate_node(alloc, type); + if (!child) return 0; + + child->parent = node; + + xml_node_struct* first_child = node->first_child; + + if (first_child) + { + xml_node_struct* last_child = first_child->prev_sibling_c; + + last_child->next_sibling = child; + child->prev_sibling_c = last_child; + first_child->prev_sibling_c = child; + } + else + { + node->first_child = child; + child->prev_sibling_c = child; + } + + return child; + } + + PUGIXML_NO_INLINE xml_attribute_struct* append_attribute_ll(xml_node_struct* node, xml_allocator& alloc) + { + xml_attribute_struct* a = allocate_attribute(alloc); + if (!a) return 0; + + xml_attribute_struct* first_attribute = node->first_attribute; + + if (first_attribute) + { + xml_attribute_struct* last_attribute = first_attribute->prev_attribute_c; + + last_attribute->next_attribute = a; + a->prev_attribute_c = last_attribute; + first_attribute->prev_attribute_c = a; + } + else + { + node->first_attribute = a; + a->prev_attribute_c = a; + } + + return a; + } +} + +// Helper classes for code generation +namespace +{ + struct opt_false + { + enum { value = 0 }; + }; + + struct opt_true + { + enum { value = 1 }; + }; +} + +// Unicode utilities +namespace +{ + 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 = (uint32_t)(ch - 0x10000) >> 10; + uint32_t lsh = (uint32_t)(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; + } + }; + + template struct wchar_selector; + + template <> struct wchar_selector<2> + { + typedef uint16_t type; + typedef utf16_counter counter; + typedef utf16_writer writer; + }; + + template <> struct wchar_selector<4> + { + typedef uint32_t type; + typedef utf32_counter counter; + typedef utf32_writer writer; + }; + + typedef wchar_selector::counter wchar_counter; + typedef wchar_selector::writer wchar_writer; + + template struct utf_decoder + { + static inline typename Traits::value_type decode_utf8_block(const uint8_t* data, size_t size, typename Traits::value_type result) + { + 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) + { + while (size >= 4 && (*reinterpret_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 ((unsigned)(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 ((unsigned)(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 ((unsigned)(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; + } + + static inline typename Traits::value_type decode_utf16_block(const uint16_t* data, size_t size, typename Traits::value_type result) + { + const uint16_t* end = data + size; + + while (data < end) + { + uint16_t lead = opt_swap::value ? endian_swap(*data) : *data; + + // U+0000..U+D7FF + if (lead < 0xD800) + { + result = Traits::low(result, lead); + data += 1; + } + // U+E000..U+FFFF + else if ((unsigned)(lead - 0xE000) < 0x2000) + { + result = Traits::low(result, lead); + data += 1; + } + // surrogate pair lead + else if ((unsigned)(lead - 0xD800) < 0x400 && data + 1 < end) + { + uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1]; + + if ((unsigned)(next - 0xDC00) < 0x400) + { + result = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff)); + data += 2; + } + else + { + data += 1; + } + } + else + { + data += 1; + } + } + + return result; + } + + static inline typename Traits::value_type decode_utf32_block(const uint32_t* data, size_t size, typename Traits::value_type result) + { + const uint32_t* end = data + size; + + while (data < end) + { + uint32_t lead = opt_swap::value ? endian_swap(*data) : *data; + + // U+0000..U+FFFF + if (lead < 0x10000) + { + result = Traits::low(result, lead); + data += 1; + } + // U+10000..U+10FFFF + else + { + result = Traits::high(result, lead); + data += 1; + } + } + + return result; + } + }; + + template inline void convert_utf_endian_swap(T* result, const T* data, size_t length) + { + for (size_t i = 0; i < length; ++i) result[i] = endian_swap(data[i]); + } + + inline 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]))); + } +} + +namespace +{ + 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, _, : + }; + + 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 (except \t), &, <, >, " + 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, _, -, . + }; + + const unsigned char chartypex_table[256] = + { + 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 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, 0, 0, 0, 0, 0, 0, 16, 16, 0, // 32-47 + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 3, 0, 3, 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 IS_CHARTYPE_IMPL(c, ct, table) ((static_cast(c) < 128 ? table[static_cast(c)] : table[128]) & (ct)) +#else +#define IS_CHARTYPE_IMPL(c, ct, table) (table[static_cast(c)] & (ct)) +#endif + +#define IS_CHARTYPE(c, ct) IS_CHARTYPE_IMPL(c, ct, chartype_table) +#define IS_CHARTYPEX(c, ct) IS_CHARTYPE_IMPL(c, ct, chartypex_table) + + bool is_little_endian() + { + unsigned int ui = 1; + + return *reinterpret_cast(&ui) == 1; + } + + xml_encoding get_wchar_encoding() + { + 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; + } + + xml_encoding guess_buffer_encoding(uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3) + { + // 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); + + DMC_VOLATILE uint8_t d0 = data[0], d1 = data[1], d2 = data[2], d3 = data[3]; + + return guess_buffer_encoding(d0, d1, d2, d3); + } + + bool get_mutable_buffer(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) + { + if (is_mutable) + { + out_buffer = static_cast(const_cast(contents)); + } + else + { + void* buffer = global_allocate(size > 0 ? size : 1); + if (!buffer) return false; + + memcpy(buffer, contents, size); + + out_buffer = static_cast(buffer); + } + + out_length = size / sizeof(char_t); + + return true; + } + +#ifdef PUGIXML_WCHAR_MODE + inline 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); + } + + 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); + + if (is_mutable) + { + out_buffer = const_cast(data); + } + else + { + out_buffer = static_cast(global_allocate(size > 0 ? size : 1)); + if (!out_buffer) return false; + } + + out_length = size / sizeof(char_t); + + convert_wchar_endian_swap(out_buffer, data, out_length); + + return true; + } + + bool convert_buffer_utf8(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size) + { + const uint8_t* data = static_cast(contents); + + // first pass: get length in wchar_t units + out_length = utf_decoder::decode_utf8_block(data, size, 0); + + // allocate buffer of suitable length + out_buffer = static_cast(global_allocate((out_length > 0 ? out_length : 1) * sizeof(char_t))); + if (!out_buffer) return false; + + // second pass: convert utf8 input to wchar_t + wchar_writer::value_type out_begin = reinterpret_cast(out_buffer); + wchar_writer::value_type out_end = utf_decoder::decode_utf8_block(data, size, out_begin); + + assert(out_end == out_begin + out_length); + (void)!out_end; + + return true; + } + + template bool convert_buffer_utf16(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) + { + const uint16_t* data = static_cast(contents); + size_t length = size / sizeof(uint16_t); + + // first pass: get length in wchar_t units + out_length = utf_decoder::decode_utf16_block(data, length, 0); + + // allocate buffer of suitable length + out_buffer = static_cast(global_allocate((out_length > 0 ? out_length : 1) * sizeof(char_t))); + if (!out_buffer) return false; + + // second pass: convert utf16 input to wchar_t + wchar_writer::value_type out_begin = reinterpret_cast(out_buffer); + wchar_writer::value_type out_end = utf_decoder::decode_utf16_block(data, length, out_begin); + + assert(out_end == out_begin + out_length); + (void)!out_end; + + return true; + } + + template bool convert_buffer_utf32(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) + { + const uint32_t* data = static_cast(contents); + size_t length = size / sizeof(uint32_t); + + // first pass: get length in wchar_t units + out_length = utf_decoder::decode_utf32_block(data, length, 0); + + // allocate buffer of suitable length + out_buffer = static_cast(global_allocate((out_length > 0 ? out_length : 1) * sizeof(char_t))); + if (!out_buffer) return false; + + // second pass: convert utf32 input to wchar_t + wchar_writer::value_type out_begin = reinterpret_cast(out_buffer); + wchar_writer::value_type out_end = utf_decoder::decode_utf32_block(data, length, out_begin); + + assert(out_end == out_begin + out_length); + (void)!out_end; + + return true; + } + + 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_utf8(out_buffer, out_length, contents, size); + + // 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_utf16(out_buffer, out_length, contents, size, opt_false()) : + convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true()); + } + + // 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_utf32(out_buffer, out_length, contents, size, opt_false()) : + convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true()); + } + + assert(!"Invalid encoding"); + return false; + } +#else + template bool convert_buffer_utf16(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) + { + const uint16_t* data = static_cast(contents); + size_t length = size / sizeof(uint16_t); + + // first pass: get length in utf8 units + out_length = utf_decoder::decode_utf16_block(data, length, 0); + + // allocate buffer of suitable length + out_buffer = static_cast(global_allocate((out_length > 0 ? out_length : 1) * sizeof(char_t))); + if (!out_buffer) return false; + + // second pass: convert utf16 input to utf8 + uint8_t* out_begin = reinterpret_cast(out_buffer); + uint8_t* out_end = utf_decoder::decode_utf16_block(data, length, out_begin); + + assert(out_end == out_begin + out_length); + (void)!out_end; + + return true; + } + + template bool convert_buffer_utf32(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, opt_swap) + { + const uint32_t* data = static_cast(contents); + size_t length = size / sizeof(uint32_t); + + // first pass: get length in utf8 units + out_length = utf_decoder::decode_utf32_block(data, length, 0); + + // allocate buffer of suitable length + out_buffer = static_cast(global_allocate((out_length > 0 ? out_length : 1) * sizeof(char_t))); + if (!out_buffer) return false; + + // second pass: convert utf32 input to utf8 + uint8_t* out_begin = reinterpret_cast(out_buffer); + uint8_t* out_end = utf_decoder::decode_utf32_block(data, length, out_begin); + + assert(out_end == out_begin + out_length); + (void)!out_end; + + return true; + } + + 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_utf16(out_buffer, out_length, contents, size, opt_false()) : + convert_buffer_utf16(out_buffer, out_length, contents, size, opt_true()); + } + + // 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_utf32(out_buffer, out_length, contents, size, opt_false()) : + convert_buffer_utf32(out_buffer, out_length, contents, size, opt_true()); + } + + assert(!"Invalid encoding"); + return false; + } +#endif + + size_t as_utf8_begin(const wchar_t* str, size_t length) + { + STATIC_ASSERT(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4); + + // get length in utf8 characters + return sizeof(wchar_t) == 2 ? + utf_decoder::decode_utf16_block(reinterpret_cast(str), length, 0) : + utf_decoder::decode_utf32_block(reinterpret_cast(str), length, 0); + } + + void as_utf8_end(char* buffer, size_t size, const wchar_t* str, size_t length) + { + STATIC_ASSERT(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4); + + // convert to utf8 + uint8_t* begin = reinterpret_cast(buffer); + uint8_t* end = sizeof(wchar_t) == 2 ? + utf_decoder::decode_utf16_block(reinterpret_cast(str), length, begin) : + utf_decoder::decode_utf32_block(reinterpret_cast(str), length, begin); + + assert(begin + size == end); + (void)!end; + + // zero-terminate + buffer[size] = 0; + } + +#ifndef PUGIXML_NO_STL + 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; + } + + std::wstring 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 = utf_decoder::decode_utf8_block(data, size, 0); + + // allocate resulting string + std::wstring 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 = utf_decoder::decode_utf8_block(data, size, begin); + + assert(begin + length == end); + (void)!end; + } + + return result; + } +#endif + + inline bool strcpy_insitu_allow(size_t length, uintptr_t allocated, char_t* target) + { + assert(target); + size_t target_length = strlength(target); + + // always reuse document buffer memory if possible + if (!allocated) 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); + } + + bool strcpy_insitu(char_t*& dest, uintptr_t& header, uintptr_t header_mask, const char_t* source) + { + size_t source_length = strlength(source); + + if (source_length == 0) + { + // empty string and null pointer are equivalent, so just deallocate old memory + xml_allocator* alloc = reinterpret_cast(header & xml_memory_page_pointer_mask)->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 + 1) * sizeof(char_t)); + + return true; + } + else + { + xml_allocator* alloc = reinterpret_cast(header & xml_memory_page_pointer_mask)->allocator; + + // 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 + 1) * sizeof(char_t)); + + // 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; + } + }; + + 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; + } + } + + return stre; + } + + // Utility macro for last character handling +#define ENDSWITH(c, e) ((c) == (e) || ((c) == 0 && endch == (e))) + + char_t* strconv_comment(char_t* s, char_t endch) + { + gap g; + + while (true) + { + while (!IS_CHARTYPE(*s, ct_parse_comment)) ++s; + + 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] == '-' && ENDSWITH(s[2], '>')) // comment ends here + { + *g.flush(s) = 0; + + return s + (s[2] == '>' ? 3 : 2); + } + else if (*s == 0) + { + return 0; + } + else ++s; + } + } + + char_t* strconv_cdata(char_t* s, char_t endch) + { + gap g; + + while (true) + { + while (!IS_CHARTYPE(*s, ct_parse_cdata)) ++s; + + 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] == ']' && 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; + + while (true) + { + while (!IS_CHARTYPE(*s, ct_parse_pcdata)) ++s; + + if (*s == '<') // PCDATA ends here + { + *g.flush(s) = 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) + { + return s; + } + else ++s; + } + } + }; + + strconv_pcdata_t get_strconv_pcdata(unsigned int optmask) + { + STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20); + + switch ((optmask >> 4) & 3) // get bitmask for flags (eol escapes) + { + 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; + default: return 0; // should not get here + } + } + + 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 (IS_CHARTYPE(*s, ct_space)) + { + char_t* str = s; + + do ++str; + while (IS_CHARTYPE(*str, ct_space)); + + g.push(s, str - s); + } + + while (true) + { + while (!IS_CHARTYPE(*s, ct_parse_attr_ws | ct_space)) ++s; + + if (*s == end_quote) + { + char_t* str = g.flush(s); + + do *str-- = 0; + while (IS_CHARTYPE(*str, ct_space)); + + return s + 1; + } + else if (IS_CHARTYPE(*s, ct_space)) + { + *s++ = ' '; + + if (IS_CHARTYPE(*s, ct_space)) + { + char_t* str = s + 1; + while (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) + { + while (!IS_CHARTYPE(*s, ct_parse_attr_ws)) ++s; + + if (*s == end_quote) + { + *g.flush(s) = 0; + + return s + 1; + } + else if (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) + { + while (!IS_CHARTYPE(*s, ct_parse_attr)) ++s; + + 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) + { + while (!IS_CHARTYPE(*s, ct_parse_attr)) ++s; + + 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; + } + } + }; + + strconv_attribute_t get_strconv_attribute(unsigned int optmask) + { + STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80); + + switch ((optmask >> 4) & 15) // get bitmask for flags (wconv wnorm eol escapes) + { + 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: return 0; // should not get here + } + } + + 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; + jmp_buf error_handler; + + // Parser utilities. +#define SKIPWS() { while (IS_CHARTYPE(*s, ct_space)) ++s; } +#define OPTSET(OPT) ( optmsk & OPT ) +#define PUSHNODE(TYPE) { cursor = append_node(cursor, alloc, TYPE); if (!cursor) THROW_ERROR(status_out_of_memory, s); } +#define POPNODE() { cursor = cursor->parent; } +#define SCANFOR(X) { while (*s != 0 && !(X)) ++s; } +#define SCANWHILE(X) { while ((X)) ++s; } +#define ENDSEG() { ch = *s; *s = 0; ++s; } +#define THROW_ERROR(err, m) error_offset = m, longjmp(error_handler, err) +#define CHECK_ERROR(err, m) { if (*s == 0) THROW_ERROR(err, m); } + + xml_parser(const xml_allocator& alloc): alloc(alloc), error_offset(0) + { + } + + // 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++; + SCANFOR(*s == ch); + if (!*s) THROW_ERROR(status_bad_doctype, s); + + s++; + } + else if (s[0] == '<' && s[1] == '?') + { + // + s += 2; + SCANFOR(s[0] == '?' && s[1] == '>'); // no need for ENDSWITH because ?> can't terminate proper doctype + if (!*s) THROW_ERROR(status_bad_doctype, s); + + s += 2; + } + else if (s[0] == '<' && s[1] == '!' && s[2] == '-' && s[3] == '-') + { + s += 4; + SCANFOR(s[0] == '-' && s[1] == '-' && s[2] == '>'); // no need for ENDSWITH because --> can't terminate proper doctype + if (!*s) THROW_ERROR(status_bad_doctype, s); + + s += 4; + } + else THROW_ERROR(status_bad_doctype, s); + + return s; + } + + char_t* parse_doctype_ignore(char_t* s) + { + assert(s[0] == '<' && s[1] == '!' && s[2] == '['); + s++; + + while (*s) + { + if (s[0] == '<' && s[1] == '!' && s[2] == '[') + { + // nested ignore section + s = parse_doctype_ignore(s); + } + else if (s[0] == ']' && s[1] == ']' && s[2] == '>') + { + // ignore section end + s += 3; + + return s; + } + else s++; + } + + THROW_ERROR(status_bad_doctype, s); + + return s; + } + + char_t* parse_doctype_group(char_t* s, char_t endch, bool toplevel) + { + assert(s[0] == '<' && s[1] == '!'); + s++; + + while (*s) + { + if (s[0] == '<' && s[1] == '!' && s[2] != '-') + { + if (s[2] == '[') + { + // ignore + s = parse_doctype_ignore(s); + } + else + { + // some control group + s = parse_doctype_group(s, endch, false); + } + } + else if (s[0] == '<' || s[0] == '"' || s[0] == '\'') + { + // unknown tag (forbidden), or some primitive group + s = parse_doctype_primitive(s); + } + else if (*s == '>') + { + s++; + + return s; + } + else s++; + } + + if (!toplevel || endch != '>') 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 (OPTSET(parse_eol) && OPTSET(parse_comments)) + { + s = strconv_comment(s, endch); + + if (!s) THROW_ERROR(status_bad_comment, cursor->value); + } + else + { + // Scan for terminating '-->'. + SCANFOR(s[0] == '-' && s[1] == '-' && ENDSWITH(s[2], '>')); + CHECK_ERROR(status_bad_comment, s); + + if (OPTSET(parse_comments)) + *s = 0; // Zero-terminate this segment at the first terminating '-'. + + s += (s[2] == '>' ? 3 : 2); // Step over the '\0->'. + } + } + else THROW_ERROR(status_bad_comment, s); + } + else if (*s == '[') + { + // 'value = s; // Save the offset. + + if (OPTSET(parse_eol)) + { + s = strconv_cdata(s, endch); + + if (!s) THROW_ERROR(status_bad_cdata, cursor->value); + } + else + { + // Scan for terminating ']]>'. + SCANFOR(s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>')); + 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 ']]>'. + SCANFOR(s[0] == ']' && s[1] == ']' && ENDSWITH(s[2], '>')); + CHECK_ERROR(status_bad_cdata, s); + + ++s; + } + + s += (s[1] == '>' ? 2 : 1); // Step over the last ']>'. + } + else 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' && ENDSWITH(s[6], 'E')) + { + s -= 2; + + if (cursor->parent) THROW_ERROR(status_bad_doctype, s); + + char_t* mark = s + 9; + + s = parse_doctype_group(s, endch, true); + + if (OPTSET(parse_doctype)) + { + while (IS_CHARTYPE(*mark, ct_space)) ++mark; + + PUSHNODE(node_doctype); + + cursor->value = mark; + + assert((s[0] == 0 && endch == '>') || s[-1] == '>'); + s[*s == 0 ? 0 : -1] = 0; + + POPNODE(); + } + } + else if (*s == 0 && endch == '-') THROW_ERROR(status_bad_comment, s); + else if (*s == 0 && endch == '[') THROW_ERROR(status_bad_cdata, s); + else 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 (!IS_CHARTYPE(*s, ct_start_symbol)) THROW_ERROR(status_bad_pi, s); + + SCANWHILE(IS_CHARTYPE(*s, ct_symbol)); + 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 ? OPTSET(parse_declaration) : OPTSET(parse_pi)) + { + if (declaration) + { + // disallow non top-level declarations + if (cursor->parent) THROW_ERROR(status_bad_pi, s); + + PUSHNODE(node_declaration); + } + else + { + PUSHNODE(node_pi); + } + + cursor->name = target; + + ENDSEG(); + + // parse value/attributes + if (ch == '?') + { + // empty node + if (!ENDSWITH(*s, '>')) THROW_ERROR(status_bad_pi, s); + s += (*s == '>'); + + POPNODE(); + } + else if (IS_CHARTYPE(ch, ct_space)) + { + SKIPWS(); + + // scan for tag end + char_t* value = s; + + SCANFOR(s[0] == '?' && ENDSWITH(s[1], '>')); + 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; + POPNODE(); + + ENDSEG(); + + s += (*s == '>'); + } + } + else THROW_ERROR(status_bad_pi, s); + } + else + { + // scan for tag end + SCANFOR(s[0] == '?' && ENDSWITH(s[1], '>')); + CHECK_ERROR(status_bad_pi, s); + + s += (s[1] == '>' ? 2 : 1); + } + + // store from registers + ref_cursor = cursor; + + return s; + } + + void parse(char_t* s, xml_node_struct* xmldoc, 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 = xmldoc; + char_t* mark = s; + + while (*s != 0) + { + if (*s == '<') + { + ++s; + +LOC_TAG: + if (IS_CHARTYPE(*s, ct_start_symbol)) // '<#...' + { + PUSHNODE(node_element); // Append a new node to the tree. + + cursor->name = s; + + SCANWHILE(IS_CHARTYPE(*s, ct_symbol)); // Scan for a terminator. + ENDSEG(); // Save char in 'ch', terminate & step over. + + if (ch == '>') + { + // end of tag + } + else if (IS_CHARTYPE(ch, ct_space)) + { +LOC_ATTRIBUTES: + while (true) + { + SKIPWS(); // Eat any whitespace. + + if (IS_CHARTYPE(*s, ct_start_symbol)) // <... #... + { + xml_attribute_struct* a = append_attribute_ll(cursor, alloc); // Make space for this attribute. + if (!a) THROW_ERROR(status_out_of_memory, s); + + a->name = s; // Save the offset. + + SCANWHILE(IS_CHARTYPE(*s, ct_symbol)); // Scan for a terminator. + CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance + + ENDSEG(); // Save char in 'ch', terminate & step over. + CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance + + if (IS_CHARTYPE(ch, ct_space)) + { + SKIPWS(); // Eat any whitespace. + CHECK_ERROR(status_bad_attribute, s); //$ redundant, left for performance + + ch = *s; + ++s; + } + + if (ch == '=') // '<... #=...' + { + 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) 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 (IS_CHARTYPE(*s, ct_start_symbol)) THROW_ERROR(status_bad_attribute, s); + } + else THROW_ERROR(status_bad_attribute, s); + } + else THROW_ERROR(status_bad_attribute, s); + } + else if (*s == '/') + { + ++s; + + if (*s == '>') + { + POPNODE(); + s++; + break; + } + else if (*s == 0 && endch == '>') + { + POPNODE(); + break; + } + else THROW_ERROR(status_bad_start_element, s); + } + else if (*s == '>') + { + ++s; + + break; + } + else if (*s == 0 && endch == '>') + { + break; + } + else THROW_ERROR(status_bad_start_element, s); + } + + // !!! + } + else if (ch == '/') // '<#.../' + { + if (!ENDSWITH(*s, '>')) THROW_ERROR(status_bad_start_element, s); + + POPNODE(); // Pop. + + s += (*s == '>'); + } + else if (ch == 0) + { + // we stepped over null terminator, backtrack & handle closing tag + --s; + + if (endch != '>') THROW_ERROR(status_bad_start_element, s); + } + else THROW_ERROR(status_bad_start_element, s); + } + else if (*s == '/') + { + ++s; + + char_t* name = cursor->name; + if (!name) THROW_ERROR(status_end_element_mismatch, s); + + while (IS_CHARTYPE(*s, ct_symbol)) + { + if (*s++ != *name++) THROW_ERROR(status_end_element_mismatch, s); + } + + if (*name) + { + if (*s == 0 && name[0] == endch && name[1] == 0) THROW_ERROR(status_bad_end_element, s); + else THROW_ERROR(status_end_element_mismatch, s); + } + + POPNODE(); // Pop. + + SKIPWS(); + + if (*s == 0) + { + if (endch != '>') THROW_ERROR(status_bad_end_element, s); + } + else + { + if (*s != '>') THROW_ERROR(status_bad_end_element, s); + ++s; + } + } + else if (*s == '?') // 'header & xml_memory_page_type_mask) + 1 == node_declaration) goto LOC_ATTRIBUTES; + } + else if (*s == '!') // 'parent) + { + PUSHNODE(node_pcdata); // Append a new node on the tree. + cursor->value = s; // Save the offset. + + s = strconv_pcdata(s); + + POPNODE(); // Pop since this is a standalone. + + if (!*s) break; + } + else + { + SCANFOR(*s == '<'); // '...<' + if (!*s) break; + + ++s; + } + + // We're after '<' + goto LOC_TAG; + } + } + + // check that last tag is closed + if (cursor != xmldoc) THROW_ERROR(status_end_element_mismatch, s); + } + + static xml_parse_result parse(char_t* buffer, size_t length, xml_node_struct* root, unsigned int optmsk) + { + xml_document_struct* xmldoc = static_cast(root); + + // store buffer for offset_debug + xmldoc->buffer = buffer; + + // early-out for empty documents + if (length == 0) return make_parse_result(status_ok); + + // create parser on stack + xml_parser parser(*xmldoc); + + // save last character and make buffer zero-terminated (speeds up parsing) + char_t endch = buffer[length - 1]; + buffer[length - 1] = 0; + + // perform actual parsing + int error = setjmp(parser.error_handler); + + if (error == 0) + { + parser.parse(buffer, xmldoc, optmsk, endch); + } + + xml_parse_result result = make_parse_result(static_cast(error), parser.error_offset ? parser.error_offset - buffer : 0); + assert(result.offset >= 0 && static_cast(result.offset) <= length); + + // update allocator state + *static_cast(xmldoc) = parser.alloc; + + // since we removed last character, we have to handle the only possible false positive + if (result && endch == '<') + { + // there's no possible well-formed document with < at the end + return make_parse_result(status_unrecognized_tag, length); + } + + return result; + } + }; + + // Output facilities + xml_encoding get_write_native_encoding() + { +#ifdef PUGIXML_WCHAR_MODE + return get_wchar_encoding(); +#else + return encoding_utf8; +#endif + } + + 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; + } + +#ifdef PUGIXML_WCHAR_MODE + size_t get_valid_length(const char_t* data, size_t length) + { + assert(length > 0); + + // discard last character if it's the lead of a surrogate pair + return (sizeof(wchar_t) == 2 && (unsigned)(static_cast(data[length - 1]) - 0xD800) < 0x400) ? length - 1 : length; + } + + size_t convert_buffer(char* result, 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(reinterpret_cast(result), data, length); + + return length * sizeof(char_t); + } + + // convert to utf8 + if (encoding == encoding_utf8) + { + uint8_t* dest = reinterpret_cast(result); + + uint8_t* end = sizeof(wchar_t) == 2 ? + utf_decoder::decode_utf16_block(reinterpret_cast(data), length, dest) : + utf_decoder::decode_utf32_block(reinterpret_cast(data), length, dest); + + return static_cast(end - dest); + } + + // convert to utf16 + if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) + { + uint16_t* dest = reinterpret_cast(result); + + // convert to native utf16 + uint16_t* end = utf_decoder::decode_utf32_block(reinterpret_cast(data), length, dest); + + // swap if necessary + xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; + + if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); + + return static_cast(end - dest) * sizeof(uint16_t); + } + + // convert to utf32 + if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) + { + uint32_t* dest = reinterpret_cast(result); + + // convert to native utf32 + uint32_t* end = utf_decoder::decode_utf16_block(reinterpret_cast(data), length, dest); + + // swap if necessary + xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; + + if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); + + return static_cast(end - dest) * sizeof(uint32_t); + } + + assert(!"Invalid encoding"); + return 0; + } +#else + size_t get_valid_length(const char_t* data, size_t length) + { + assert(length > 4); + + 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; + } + + size_t convert_buffer(char* result, const char_t* data, size_t length, xml_encoding encoding) + { + if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) + { + uint16_t* dest = reinterpret_cast(result); + + // convert to native utf16 + uint16_t* end = utf_decoder::decode_utf8_block(reinterpret_cast(data), length, dest); + + // swap if necessary + xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; + + if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); + + return static_cast(end - dest) * sizeof(uint16_t); + } + + if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) + { + uint32_t* dest = reinterpret_cast(result); + + // convert to native utf32 + uint32_t* end = utf_decoder::decode_utf8_block(reinterpret_cast(data), length, dest); + + // swap if necessary + xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; + + if (native_encoding != encoding) convert_utf_endian_swap(dest, dest, static_cast(end - dest)); + + return static_cast(end - dest) * sizeof(uint32_t); + } + + assert(!"Invalid encoding"); + 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)) + { + } + + ~xml_buffered_writer() + { + flush(); + } + + void flush() + { + flush(buffer, bufsize); + bufsize = 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(scratch, data, size, encoding); + assert(result <= sizeof(scratch)); + + // write data + writer.write(scratch, result); + } + } + + void write(const char_t* data, size_t length) + { + if (bufsize + length > bufcapacity) + { + // 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); + + // 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(const char_t* data) + { + write(data, strlength(data)); + } + + void write(char_t d0) + { + if (bufsize + 1 > bufcapacity) flush(); + + buffer[bufsize + 0] = d0; + bufsize += 1; + } + + void write(char_t d0, char_t d1) + { + if (bufsize + 2 > bufcapacity) flush(); + + buffer[bufsize + 0] = d0; + buffer[bufsize + 1] = d1; + bufsize += 2; + } + + void write(char_t d0, char_t d1, char_t d2) + { + if (bufsize + 3 > bufcapacity) flush(); + + buffer[bufsize + 0] = d0; + buffer[bufsize + 1] = d1; + buffer[bufsize + 2] = d2; + bufsize += 3; + } + + void write(char_t d0, char_t d1, char_t d2, char_t d3) + { + if (bufsize + 4 > bufcapacity) flush(); + + buffer[bufsize + 0] = d0; + buffer[bufsize + 1] = d1; + buffer[bufsize + 2] = d2; + buffer[bufsize + 3] = d3; + bufsize += 4; + } + + void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4) + { + if (bufsize + 5 > bufcapacity) flush(); + + buffer[bufsize + 0] = d0; + buffer[bufsize + 1] = d1; + buffer[bufsize + 2] = d2; + buffer[bufsize + 3] = d3; + buffer[bufsize + 4] = d4; + bufsize += 5; + } + + void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4, char_t d5) + { + if (bufsize + 6 > bufcapacity) flush(); + + buffer[bufsize + 0] = d0; + buffer[bufsize + 1] = d1; + buffer[bufsize + 2] = d2; + buffer[bufsize + 3] = d3; + buffer[bufsize + 4] = d4; + buffer[bufsize + 5] = d5; + bufsize += 6; + } + + // utf8 maximum expansion: x4 (-> utf32) + // utf16 maximum expansion: x2 (-> utf32) + // utf32 maximum expansion: x1 + enum { bufcapacity = 2048 }; + + char_t buffer[bufcapacity]; + char scratch[4 * bufcapacity]; + + xml_writer& writer; + size_t bufsize; + xml_encoding encoding; + }; + + void write_bom(xml_writer& writer, xml_encoding encoding) + { + switch (encoding) + { + case encoding_utf8: + writer.write("\xef\xbb\xbf", 3); + break; + + case encoding_utf16_be: + writer.write("\xfe\xff", 2); + break; + + case encoding_utf16_le: + writer.write("\xff\xfe", 2); + break; + + case encoding_utf32_be: + writer.write("\x00\x00\xfe\xff", 4); + break; + + case encoding_utf32_le: + writer.write("\xff\xfe\x00\x00", 4); + break; + + default: + assert(!"Invalid encoding"); + } + } + + void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type) + { + while (*s) + { + const char_t* prev = s; + + // While *s is a usual symbol + while (!IS_CHARTYPEX(*s, type)) ++s; + + writer.write(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 '"': + writer.write('&', 'q', 'u', 'o', 't', ';'); + ++s; + break; + default: // s is not a usual symbol + { + unsigned int ch = static_cast(*s++); + assert(ch < 32); + + writer.write('&', '#', static_cast((ch / 10) + '0'), static_cast((ch % 10) + '0'), ';'); + } + } + } + } + + 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(prev, static_cast(s - prev)); + + writer.write(']', ']', '>'); + } + while (*s); + } + + void node_output_attributes(xml_buffered_writer& writer, const xml_node& node) + { + const char_t* default_name = PUGIXML_TEXT(":anonymous"); + + for (xml_attribute a = node.first_attribute(); a; a = a.next_attribute()) + { + writer.write(' '); + writer.write(a.name()[0] ? a.name() : default_name); + writer.write('=', '"'); + + text_output_escaped(writer, a.value(), ctx_special_attr); + + writer.write('"'); + } + } + + void node_output(xml_buffered_writer& writer, const xml_node& node, const char_t* indent, unsigned int flags, unsigned int depth) + { + const char_t* default_name = PUGIXML_TEXT(":anonymous"); + + if ((flags & format_indent) != 0 && (flags & format_raw) == 0) + for (unsigned int i = 0; i < depth; ++i) writer.write(indent); + + switch (node.type()) + { + case node_document: + { + for (xml_node n = node.first_child(); n; n = n.next_sibling()) + node_output(writer, n, indent, flags, depth); + break; + } + + case node_element: + { + const char_t* name = node.name()[0] ? node.name() : default_name; + + writer.write('<'); + writer.write(name); + + node_output_attributes(writer, node); + + if (flags & format_raw) + { + if (!node.first_child()) + writer.write(' ', '/', '>'); + else + { + writer.write('>'); + + for (xml_node n = node.first_child(); n; n = n.next_sibling()) + node_output(writer, n, indent, flags, depth + 1); + + writer.write('<', '/'); + writer.write(name); + writer.write('>'); + } + } + else if (!node.first_child()) + writer.write(' ', '/', '>', '\n'); + else if (node.first_child() == node.last_child() && (node.first_child().type() == node_pcdata || node.first_child().type() == node_cdata)) + { + writer.write('>'); + + if (node.first_child().type() == node_pcdata) + text_output_escaped(writer, node.first_child().value(), ctx_special_pcdata); + else + text_output_cdata(writer, node.first_child().value()); + + writer.write('<', '/'); + writer.write(name); + writer.write('>', '\n'); + } + else + { + writer.write('>', '\n'); + + for (xml_node n = node.first_child(); n; n = n.next_sibling()) + node_output(writer, n, indent, flags, depth + 1); + + if ((flags & format_indent) != 0 && (flags & format_raw) == 0) + for (unsigned int i = 0; i < depth; ++i) writer.write(indent); + + writer.write('<', '/'); + writer.write(name); + writer.write('>', '\n'); + } + + break; + } + + case node_pcdata: + text_output_escaped(writer, node.value(), ctx_special_pcdata); + if ((flags & format_raw) == 0) writer.write('\n'); + break; + + case node_cdata: + text_output_cdata(writer, node.value()); + if ((flags & format_raw) == 0) writer.write('\n'); + break; + + case node_comment: + writer.write('<', '!', '-', '-'); + writer.write(node.value()); + writer.write('-', '-', '>'); + if ((flags & format_raw) == 0) writer.write('\n'); + break; + + case node_pi: + case node_declaration: + writer.write('<', '?'); + writer.write(node.name()[0] ? node.name() : default_name); + + if (node.type() == node_declaration) + { + node_output_attributes(writer, node); + } + else if (node.value()[0]) + { + writer.write(' '); + writer.write(node.value()); + } + + writer.write('?', '>'); + if ((flags & format_raw) == 0) writer.write('\n'); + break; + + case node_doctype: + writer.write('<', '!', 'D', 'O', 'C'); + writer.write('T', 'Y', 'P', 'E'); + + if (node.value()[0]) + { + writer.write(' '); + writer.write(node.value()); + } + + writer.write('>'); + if ((flags & format_raw) == 0) writer.write('\n'); + break; + + default: + assert(!"Invalid node type"); + } + } + + inline bool has_declaration(const xml_node& node) + { + for (xml_node child = node.first_child(); child; child = child.next_sibling()) + { + xml_node_type type = child.type(); + + if (type == node_declaration) return true; + if (type == node_element) return false; + } + + return false; + } + + inline 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; + } + + void recursive_copy_skip(xml_node& dest, const xml_node& source, const xml_node& skip) + { + assert(dest.type() == source.type()); + + switch (source.type()) + { + case node_element: + { + dest.set_name(source.name()); + + for (xml_attribute a = source.first_attribute(); a; a = a.next_attribute()) + dest.append_attribute(a.name()).set_value(a.value()); + + for (xml_node c = source.first_child(); c; c = c.next_sibling()) + { + if (c == skip) continue; + + xml_node cc = dest.append_child(c.type()); + assert(cc); + + recursive_copy_skip(cc, c, skip); + } + + break; + } + + case node_pcdata: + case node_cdata: + case node_comment: + case node_doctype: + dest.set_value(source.value()); + break; + + case node_pi: + dest.set_name(source.name()); + dest.set_value(source.value()); + break; + + case node_declaration: + { + dest.set_name(source.name()); + + for (xml_attribute a = source.first_attribute(); a; a = a.next_attribute()) + dest.append_attribute(a.name()).set_value(a.value()); + + break; + } + + default: + assert(!"Invalid node type"); + } + } + + // 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 + xml_parse_status get_file_size(FILE* file, size_t& out_result) + { +#if defined(_MSC_VER) && _MSC_VER >= 1400 + // 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__) + // 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; + } + + xml_parse_result load_file_impl(xml_document& doc, FILE* file, unsigned int options, xml_encoding encoding) + { + 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) + { + fclose(file); + return make_parse_result(size_status); + } + + // allocate buffer for the whole file + char* contents = static_cast(global_allocate(size > 0 ? size : 1)); + + if (!contents) + { + fclose(file); + return make_parse_result(status_out_of_memory); + } + + // read file in memory + size_t read_size = fread(contents, 1, size, file); + fclose(file); + + if (read_size != size) + { + global_deallocate(contents); + return make_parse_result(status_io_error); + } + + return doc.load_buffer_inplace_own(contents, size, options, encoding); + } + +#ifndef PUGIXML_NO_STL + template xml_parse_result load_stream_impl(xml_document& doc, std::basic_istream& stream, unsigned int options, xml_encoding encoding) + { + // 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 make_parse_result(status_io_error); + + // guard against huge files + size_t read_length = static_cast(length); + + if (static_cast(read_length) != length || length < 0) return make_parse_result(status_out_of_memory); + + // read stream data into memory (guard against stream exceptions with buffer holder) + buffer_holder buffer(global_allocate((read_length > 0 ? read_length : 1) * sizeof(T)), global_deallocate); + if (!buffer.data) return make_parse_result(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()) return make_parse_result(status_io_error); + + // load data from buffer + size_t actual_length = static_cast(stream.gcount()); + assert(actual_length <= read_length); + + return doc.load_buffer_inplace_own(buffer.release(), actual_length * sizeof(T), options, encoding); + } +#endif + +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__MINGW32__) + FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) + { + return _wfopen(path, mode); + } +#else + char* convert_path_heap(const wchar_t* str) + { + assert(str); + + // first pass: get length in utf8 characters + size_t length = wcslen(str); + size_t size = as_utf8_begin(str, length); + + // allocate resulting string + char* result = static_cast(global_allocate(size + 1)); + if (!result) return 0; + + // second pass: convert to utf8 + as_utf8_end(result, size, str, length); + + return result; + } + + 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 + global_deallocate(path_utf8); + + return result; + } +#endif +} + +namespace pugi +{ + xml_writer_file::xml_writer_file(void* file): file(file) + { + } + + void xml_writer_file::write(const void* data, size_t size) + { + fwrite(data, size, 1, static_cast(file)); + } + +#ifndef PUGIXML_NO_STL + xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(&stream), wide_stream(0) + { + } + + xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(0), wide_stream(&stream) + { + } + + 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 + + xml_tree_walker::xml_tree_walker(): _depth(0) + { + } + + xml_tree_walker::~xml_tree_walker() + { + } + + int xml_tree_walker::depth() const + { + return _depth; + } + + bool xml_tree_walker::begin(xml_node&) + { + return true; + } + + bool xml_tree_walker::end(xml_node&) + { + return true; + } + + xml_attribute::xml_attribute(): _attr(0) + { + } + + xml_attribute::xml_attribute(xml_attribute_struct* attr): _attr(attr) + { + } + + xml_attribute::operator xml_attribute::unspecified_bool_type() const + { + return _attr ? &xml_attribute::_attr : 0; + } + + bool xml_attribute::operator!() const + { + return !_attr; + } + + bool xml_attribute::operator==(const xml_attribute& r) const + { + return (_attr == r._attr); + } + + bool xml_attribute::operator!=(const xml_attribute& r) const + { + return (_attr != r._attr); + } + + bool xml_attribute::operator<(const xml_attribute& r) const + { + return (_attr < r._attr); + } + + bool xml_attribute::operator>(const xml_attribute& r) const + { + return (_attr > r._attr); + } + + bool xml_attribute::operator<=(const xml_attribute& r) const + { + return (_attr <= r._attr); + } + + bool xml_attribute::operator>=(const xml_attribute& r) const + { + return (_attr >= r._attr); + } + + xml_attribute xml_attribute::next_attribute() const + { + return _attr ? xml_attribute(_attr->next_attribute) : xml_attribute(); + } + + xml_attribute xml_attribute::previous_attribute() const + { + return _attr && _attr->prev_attribute_c->next_attribute ? xml_attribute(_attr->prev_attribute_c) : xml_attribute(); + } + + int xml_attribute::as_int() const + { + if (!_attr || !_attr->value) return 0; + +#ifdef PUGIXML_WCHAR_MODE + return (int)wcstol(_attr->value, 0, 10); +#else + return (int)strtol(_attr->value, 0, 10); +#endif + } + + unsigned int xml_attribute::as_uint() const + { + if (!_attr || !_attr->value) return 0; + +#ifdef PUGIXML_WCHAR_MODE + return (unsigned int)wcstoul(_attr->value, 0, 10); +#else + return (unsigned int)strtoul(_attr->value, 0, 10); +#endif + } + + double xml_attribute::as_double() const + { + if (!_attr || !_attr->value) return 0; + +#ifdef PUGIXML_WCHAR_MODE + return wcstod(_attr->value, 0); +#else + return strtod(_attr->value, 0); +#endif + } + + float xml_attribute::as_float() const + { + if (!_attr || !_attr->value) return 0; + +#ifdef PUGIXML_WCHAR_MODE + return (float)wcstod(_attr->value, 0); +#else + return (float)strtod(_attr->value, 0); +#endif + } + + bool xml_attribute::as_bool() const + { + if (!_attr || !_attr->value) return false; + + // only look at first char + char_t first = *_attr->value; + + // 1*, t* (true), T* (True), y* (yes), Y* (YES) + return (first == '1' || first == 't' || first == 'T' || first == 'y' || first == 'Y'); + } + + bool xml_attribute::empty() const + { + return !_attr; + } + + const char_t* xml_attribute::name() const + { + return (_attr && _attr->name) ? _attr->name : PUGIXML_TEXT(""); + } + + const char_t* xml_attribute::value() const + { + return (_attr && _attr->value) ? _attr->value : PUGIXML_TEXT(""); + } + + size_t xml_attribute::hash_value() const + { + return static_cast(reinterpret_cast(_attr) / sizeof(xml_attribute_struct)); + } + + xml_attribute_struct* xml_attribute::internal_object() const + { + return _attr; + } + + xml_attribute& xml_attribute::operator=(const char_t* rhs) + { + set_value(rhs); + return *this; + } + + xml_attribute& xml_attribute::operator=(int rhs) + { + set_value(rhs); + return *this; + } + + xml_attribute& xml_attribute::operator=(unsigned int rhs) + { + set_value(rhs); + return *this; + } + + xml_attribute& xml_attribute::operator=(double rhs) + { + set_value(rhs); + return *this; + } + + xml_attribute& xml_attribute::operator=(bool rhs) + { + set_value(rhs); + return *this; + } + + bool xml_attribute::set_name(const char_t* rhs) + { + if (!_attr) return false; + + return strcpy_insitu(_attr->name, _attr->header, xml_memory_page_name_allocated_mask, rhs); + } + + bool xml_attribute::set_value(const char_t* rhs) + { + if (!_attr) return false; + + return strcpy_insitu(_attr->value, _attr->header, xml_memory_page_value_allocated_mask, rhs); + } + + bool xml_attribute::set_value(int rhs) + { + char buf[128]; + sprintf(buf, "%d", rhs); + +#ifdef PUGIXML_WCHAR_MODE + char_t wbuf[128]; + widen_ascii(wbuf, buf); + + return set_value(wbuf); +#else + return set_value(buf); +#endif + } + + bool xml_attribute::set_value(unsigned int rhs) + { + char buf[128]; + sprintf(buf, "%u", rhs); + +#ifdef PUGIXML_WCHAR_MODE + char_t wbuf[128]; + widen_ascii(wbuf, buf); + + return set_value(wbuf); +#else + return set_value(buf); +#endif + } + + bool xml_attribute::set_value(double rhs) + { + char buf[128]; + sprintf(buf, "%g", rhs); + +#ifdef PUGIXML_WCHAR_MODE + char_t wbuf[128]; + widen_ascii(wbuf, buf); + + return set_value(wbuf); +#else + return set_value(buf); +#endif + } + + bool xml_attribute::set_value(bool rhs) + { + return set_value(rhs ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false")); + } + +#ifdef __BORLANDC__ + bool operator&&(const xml_attribute& lhs, bool rhs) + { + return (bool)lhs && rhs; + } + + bool operator||(const xml_attribute& lhs, bool rhs) + { + return (bool)lhs || rhs; + } +#endif + + xml_node::xml_node(): _root(0) + { + } + + xml_node::xml_node(xml_node_struct* p): _root(p) + { + } + + xml_node::operator xml_node::unspecified_bool_type() const + { + return _root ? &xml_node::_root : 0; + } + + bool xml_node::operator!() const + { + return !_root; + } + + xml_node::iterator xml_node::begin() const + { + return iterator(_root ? _root->first_child : 0, _root); + } + + xml_node::iterator xml_node::end() const + { + return iterator(0, _root); + } + + xml_node::attribute_iterator xml_node::attributes_begin() const + { + return attribute_iterator(_root ? _root->first_attribute : 0, _root); + } + + xml_node::attribute_iterator xml_node::attributes_end() const + { + return attribute_iterator(0, _root); + } + + bool xml_node::operator==(const xml_node& r) const + { + return (_root == r._root); + } + + bool xml_node::operator!=(const xml_node& r) const + { + return (_root != r._root); + } + + bool xml_node::operator<(const xml_node& r) const + { + return (_root < r._root); + } + + bool xml_node::operator>(const xml_node& r) const + { + return (_root > r._root); + } + + bool xml_node::operator<=(const xml_node& r) const + { + return (_root <= r._root); + } + + bool xml_node::operator>=(const xml_node& r) const + { + return (_root >= r._root); + } + + bool xml_node::empty() const + { + return !_root; + } + + const char_t* xml_node::name() const + { + return (_root && _root->name) ? _root->name : PUGIXML_TEXT(""); + } + + xml_node_type xml_node::type() const + { + return _root ? static_cast((_root->header & xml_memory_page_type_mask) + 1) : node_null; + } + + const char_t* xml_node::value() const + { + return (_root && _root->value) ? _root->value : PUGIXML_TEXT(""); + } + + 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 && strequal(name, i->name)) return xml_node(i); + + return xml_node(); + } + + 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 && strequal(name, i->name)) + return xml_attribute(i); + + return xml_attribute(); + } + + 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 && strequal(name, i->name)) return xml_node(i); + + return xml_node(); + } + + xml_node xml_node::next_sibling() const + { + if (!_root) return xml_node(); + + if (_root->next_sibling) return xml_node(_root->next_sibling); + else return xml_node(); + } + + 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 && strequal(name, i->name)) return xml_node(i); + + return xml_node(); + } + + 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(); + } + + xml_node xml_node::parent() const + { + return _root ? xml_node(_root->parent) : xml_node(); + } + + xml_node xml_node::root() const + { + if (!_root) return xml_node(); + + xml_memory_page* page = reinterpret_cast(_root->header & xml_memory_page_pointer_mask); + + return xml_node(static_cast(page->allocator)); + } + + const char_t* xml_node::child_value() const + { + if (!_root) return PUGIXML_TEXT(""); + + for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) + { + xml_node_type type = static_cast((i->header & xml_memory_page_type_mask) + 1); + + if (i->value && (type == node_pcdata || type == node_cdata)) + return i->value; + } + + return PUGIXML_TEXT(""); + } + + const char_t* xml_node::child_value(const char_t* name) const + { + return child(name).child_value(); + } + + xml_attribute xml_node::first_attribute() const + { + return _root ? xml_attribute(_root->first_attribute) : xml_attribute(); + } + + xml_attribute xml_node::last_attribute() const + { + return _root && _root->first_attribute ? xml_attribute(_root->first_attribute->prev_attribute_c) : xml_attribute(); + } + + xml_node xml_node::first_child() const + { + return _root ? xml_node(_root->first_child) : xml_node(); + } + + xml_node xml_node::last_child() const + { + return _root && _root->first_child ? xml_node(_root->first_child->prev_sibling_c) : xml_node(); + } + + bool xml_node::set_name(const char_t* rhs) + { + switch (type()) + { + case node_pi: + case node_declaration: + case node_element: + return strcpy_insitu(_root->name, _root->header, xml_memory_page_name_allocated_mask, rhs); + + default: + return false; + } + } + + bool xml_node::set_value(const char_t* rhs) + { + switch (type()) + { + case node_pi: + case node_cdata: + case node_pcdata: + case node_comment: + case node_doctype: + return strcpy_insitu(_root->value, _root->header, xml_memory_page_value_allocated_mask, rhs); + + default: + return false; + } + } + + xml_attribute xml_node::append_attribute(const char_t* name) + { + if (type() != node_element && type() != node_declaration) return xml_attribute(); + + xml_attribute a(append_attribute_ll(_root, get_allocator(_root))); + a.set_name(name); + + return a; + } + + xml_attribute xml_node::prepend_attribute(const char_t* name) + { + if (type() != node_element && type() != node_declaration) return xml_attribute(); + + xml_attribute a(allocate_attribute(get_allocator(_root))); + if (!a) return xml_attribute(); + + a.set_name(name); + + xml_attribute_struct* head = _root->first_attribute; + + if (head) + { + a._attr->prev_attribute_c = head->prev_attribute_c; + head->prev_attribute_c = a._attr; + } + else + a._attr->prev_attribute_c = a._attr; + + a._attr->next_attribute = head; + _root->first_attribute = a._attr; + + return a; + } + + xml_attribute xml_node::insert_attribute_before(const char_t* name, const xml_attribute& attr) + { + if ((type() != node_element && type() != node_declaration) || attr.empty()) return xml_attribute(); + + // check that attribute belongs to *this + xml_attribute_struct* cur = attr._attr; + + while (cur->prev_attribute_c->next_attribute) cur = cur->prev_attribute_c; + + if (cur != _root->first_attribute) return xml_attribute(); + + xml_attribute a(allocate_attribute(get_allocator(_root))); + if (!a) return xml_attribute(); + + a.set_name(name); + + if (attr._attr->prev_attribute_c->next_attribute) + attr._attr->prev_attribute_c->next_attribute = a._attr; + else + _root->first_attribute = a._attr; + + a._attr->prev_attribute_c = attr._attr->prev_attribute_c; + a._attr->next_attribute = attr._attr; + attr._attr->prev_attribute_c = a._attr; + + return a; + } + + xml_attribute xml_node::insert_attribute_after(const char_t* name, const xml_attribute& attr) + { + if ((type() != node_element && type() != node_declaration) || attr.empty()) return xml_attribute(); + + // check that attribute belongs to *this + xml_attribute_struct* cur = attr._attr; + + while (cur->prev_attribute_c->next_attribute) cur = cur->prev_attribute_c; + + if (cur != _root->first_attribute) return xml_attribute(); + + xml_attribute a(allocate_attribute(get_allocator(_root))); + if (!a) return xml_attribute(); + + a.set_name(name); + + if (attr._attr->next_attribute) + attr._attr->next_attribute->prev_attribute_c = a._attr; + else + _root->first_attribute->prev_attribute_c = a._attr; + + a._attr->next_attribute = attr._attr->next_attribute; + a._attr->prev_attribute_c = attr._attr; + attr._attr->next_attribute = a._attr; + + return a; + } + + xml_attribute xml_node::append_copy(const xml_attribute& proto) + { + if (!proto) return xml_attribute(); + + xml_attribute result = append_attribute(proto.name()); + result.set_value(proto.value()); + + return result; + } + + xml_attribute xml_node::prepend_copy(const xml_attribute& proto) + { + if (!proto) return xml_attribute(); + + xml_attribute result = prepend_attribute(proto.name()); + result.set_value(proto.value()); + + return result; + } + + xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr) + { + if (!proto) return xml_attribute(); + + xml_attribute result = insert_attribute_after(proto.name(), attr); + result.set_value(proto.value()); + + return result; + } + + xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr) + { + if (!proto) return xml_attribute(); + + xml_attribute result = insert_attribute_before(proto.name(), attr); + result.set_value(proto.value()); + + return result; + } + + xml_node xml_node::append_child(xml_node_type type) + { + if (!allow_insert_child(this->type(), type)) return xml_node(); + + xml_node n(append_node(_root, get_allocator(_root), type)); + + if (type == node_declaration) n.set_name(PUGIXML_TEXT("xml")); + + return n; + } + + xml_node xml_node::prepend_child(xml_node_type type) + { + if (!allow_insert_child(this->type(), type)) return xml_node(); + + xml_node n(allocate_node(get_allocator(_root), type)); + if (!n) return xml_node(); + + n._root->parent = _root; + + xml_node_struct* head = _root->first_child; + + if (head) + { + n._root->prev_sibling_c = head->prev_sibling_c; + head->prev_sibling_c = n._root; + } + else + n._root->prev_sibling_c = n._root; + + n._root->next_sibling = head; + _root->first_child = n._root; + + if (type == node_declaration) n.set_name(PUGIXML_TEXT("xml")); + + return n; + } + + xml_node xml_node::insert_child_before(xml_node_type type, const xml_node& node) + { + if (!allow_insert_child(this->type(), type)) return xml_node(); + if (!node._root || node._root->parent != _root) return xml_node(); + + xml_node n(allocate_node(get_allocator(_root), type)); + if (!n) return xml_node(); + + n._root->parent = _root; + + if (node._root->prev_sibling_c->next_sibling) + node._root->prev_sibling_c->next_sibling = n._root; + else + _root->first_child = n._root; + + n._root->prev_sibling_c = node._root->prev_sibling_c; + n._root->next_sibling = node._root; + node._root->prev_sibling_c = n._root; + + if (type == node_declaration) n.set_name(PUGIXML_TEXT("xml")); + + return n; + } + + xml_node xml_node::insert_child_after(xml_node_type type, const xml_node& node) + { + if (!allow_insert_child(this->type(), type)) return xml_node(); + if (!node._root || node._root->parent != _root) return xml_node(); + + xml_node n(allocate_node(get_allocator(_root), type)); + if (!n) return xml_node(); + + n._root->parent = _root; + + if (node._root->next_sibling) + node._root->next_sibling->prev_sibling_c = n._root; + else + _root->first_child->prev_sibling_c = n._root; + + n._root->next_sibling = node._root->next_sibling; + n._root->prev_sibling_c = node._root; + node._root->next_sibling = n._root; + + if (type == node_declaration) n.set_name(PUGIXML_TEXT("xml")); + + return n; + } + + xml_node xml_node::append_child(const char_t* name) + { + xml_node result = append_child(node_element); + + result.set_name(name); + + return result; + } + + xml_node xml_node::prepend_child(const char_t* name) + { + xml_node result = prepend_child(node_element); + + result.set_name(name); + + return result; + } + + 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; + } + + 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; + } + + xml_node xml_node::append_copy(const xml_node& proto) + { + xml_node result = append_child(proto.type()); + + if (result) recursive_copy_skip(result, proto, result); + + return result; + } + + xml_node xml_node::prepend_copy(const xml_node& proto) + { + xml_node result = prepend_child(proto.type()); + + if (result) recursive_copy_skip(result, proto, result); + + return result; + } + + xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node) + { + xml_node result = insert_child_after(proto.type(), node); + + if (result) recursive_copy_skip(result, proto, result); + + return result; + } + + xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node) + { + xml_node result = insert_child_before(proto.type(), node); + + if (result) recursive_copy_skip(result, proto, result); + + return result; + } + + bool xml_node::remove_attribute(const char_t* name) + { + return remove_attribute(attribute(name)); + } + + bool xml_node::remove_attribute(const xml_attribute& a) + { + if (!_root || !a._attr) return false; + + // check that attribute belongs to *this + xml_attribute_struct* attr = a._attr; + + while (attr->prev_attribute_c->next_attribute) attr = attr->prev_attribute_c; + + if (attr != _root->first_attribute) return false; + + if (a._attr->next_attribute) a._attr->next_attribute->prev_attribute_c = a._attr->prev_attribute_c; + else if (_root->first_attribute) _root->first_attribute->prev_attribute_c = a._attr->prev_attribute_c; + + if (a._attr->prev_attribute_c->next_attribute) a._attr->prev_attribute_c->next_attribute = a._attr->next_attribute; + else _root->first_attribute = a._attr->next_attribute; + + destroy_attribute(a._attr, get_allocator(_root)); + + return true; + } + + bool xml_node::remove_child(const char_t* name) + { + return remove_child(child(name)); + } + + bool xml_node::remove_child(const xml_node& n) + { + if (!_root || !n._root || n._root->parent != _root) return false; + + if (n._root->next_sibling) n._root->next_sibling->prev_sibling_c = n._root->prev_sibling_c; + else if (_root->first_child) _root->first_child->prev_sibling_c = n._root->prev_sibling_c; + + if (n._root->prev_sibling_c->next_sibling) n._root->prev_sibling_c->next_sibling = n._root->next_sibling; + else _root->first_child = n._root->next_sibling; + + destroy_node(n._root, get_allocator(_root)); + + return true; + } + + 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 && strequal(name, i->name)) + { + for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) + if (strequal(attr_name, a->name) && strequal(attr_value, a->value)) + return xml_node(i); + } + + return xml_node(); + } + + 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 (strequal(attr_name, a->name) && strequal(attr_value, a->value)) + return xml_node(i); + + return xml_node(); + } + +#ifndef PUGIXML_NO_STL + string_t xml_node::path(char_t delimiter) const + { + string_t path; + + xml_node cursor = *this; // Make a copy. + + path = cursor.name(); + + while (cursor.parent()) + { + cursor = cursor.parent(); + + string_t temp = cursor.name(); + temp += delimiter; + temp += path; + path.swap(temp); + } + + return path; + } +#endif + + 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 || !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 && 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(); + } + } + + bool xml_node::traverse(xml_tree_walker& walker) + { + walker._depth = -1; + + xml_node arg_begin = *this; + if (!walker.begin(arg_begin)) return false; + + xml_node cur = first_child(); + + 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 + { + // Borland C++ workaround + while (!cur.next_sibling() && cur != *this && (bool)cur.parent()) + { + --walker._depth; + cur = cur.parent(); + } + + if (cur != *this) + cur = cur.next_sibling(); + } + } + while (cur && cur != *this); + } + + assert(walker._depth == -1); + + xml_node arg_end = *this; + return walker.end(arg_end); + } + + size_t xml_node::hash_value() const + { + return static_cast(reinterpret_cast(_root) / sizeof(xml_node_struct)); + } + + xml_node_struct* xml_node::internal_object() const + { + return _root; + } + + void xml_node::print(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const + { + if (!_root) return; + + xml_buffered_writer buffered_writer(writer, encoding); + + node_output(buffered_writer, *this, indent, flags, depth); + } + +#ifndef PUGIXML_NO_STL + 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); + } + + 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 + + ptrdiff_t xml_node::offset_debug() const + { + xml_node_struct* r = root()._root; + + if (!r) return -1; + + const char_t* buffer = static_cast(r)->buffer; + + if (!buffer) return -1; + + switch (type()) + { + case node_document: + return 0; + + case node_element: + case node_declaration: + case node_pi: + return (_root->header & xml_memory_page_name_allocated_mask) ? -1 : _root->name - buffer; + + case node_pcdata: + case node_cdata: + case node_comment: + case node_doctype: + return (_root->header & xml_memory_page_value_allocated_mask) ? -1 : _root->value - buffer; + + default: + return -1; + } + } + +#ifdef __BORLANDC__ + bool operator&&(const xml_node& lhs, bool rhs) + { + return (bool)lhs && rhs; + } + + bool operator||(const xml_node& lhs, bool rhs) + { + return (bool)lhs || rhs; + } +#endif + + xml_node_iterator::xml_node_iterator() + { + } + + xml_node_iterator::xml_node_iterator(const xml_node& node): _wrap(node), _parent(node.parent()) + { + } + + xml_node_iterator::xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) + { + } + + bool xml_node_iterator::operator==(const xml_node_iterator& rhs) const + { + return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; + } + + bool xml_node_iterator::operator!=(const xml_node_iterator& rhs) const + { + return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; + } + + xml_node& xml_node_iterator::operator*() + { + assert(_wrap._root); + return _wrap; + } + + xml_node* xml_node_iterator::operator->() + { + assert(_wrap._root); + return &_wrap; + } + + const xml_node_iterator& xml_node_iterator::operator++() + { + assert(_wrap._root); + _wrap._root = _wrap._root->next_sibling; + return *this; + } + + xml_node_iterator xml_node_iterator::operator++(int) + { + xml_node_iterator temp = *this; + ++*this; + return temp; + } + + const xml_node_iterator& xml_node_iterator::operator--() + { + _wrap = _wrap._root ? _wrap.previous_sibling() : _parent.last_child(); + return *this; + } + + xml_node_iterator xml_node_iterator::operator--(int) + { + xml_node_iterator temp = *this; + --*this; + return temp; + } + + xml_attribute_iterator::xml_attribute_iterator() + { + } + + xml_attribute_iterator::xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent): _wrap(attr), _parent(parent) + { + } + + xml_attribute_iterator::xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) + { + } + + bool xml_attribute_iterator::operator==(const xml_attribute_iterator& rhs) const + { + return _wrap._attr == rhs._wrap._attr && _parent._root == rhs._parent._root; + } + + bool xml_attribute_iterator::operator!=(const xml_attribute_iterator& rhs) const + { + return _wrap._attr != rhs._wrap._attr || _parent._root != rhs._parent._root; + } + + xml_attribute& xml_attribute_iterator::operator*() + { + assert(_wrap._attr); + return _wrap; + } + + xml_attribute* xml_attribute_iterator::operator->() + { + assert(_wrap._attr); + return &_wrap; + } + + const xml_attribute_iterator& xml_attribute_iterator::operator++() + { + assert(_wrap._attr); + _wrap._attr = _wrap._attr->next_attribute; + return *this; + } + + xml_attribute_iterator xml_attribute_iterator::operator++(int) + { + xml_attribute_iterator temp = *this; + ++*this; + return temp; + } + + const xml_attribute_iterator& xml_attribute_iterator::operator--() + { + _wrap = _wrap._attr ? _wrap.previous_attribute() : _parent.last_attribute(); + return *this; + } + + xml_attribute_iterator xml_attribute_iterator::operator--(int) + { + xml_attribute_iterator temp = *this; + --*this; + return temp; + } + + xml_parse_result::xml_parse_result(): status(status_internal_error), offset(0), encoding(encoding_auto) + { + } + + xml_parse_result::operator bool() const + { + return status == status_ok; + } + + 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"; + + default: return "Unknown error"; + } + } + + xml_document::xml_document(): _buffer(0) + { + create(); + } + + xml_document::~xml_document() + { + destroy(); + } + + void xml_document::reset() + { + destroy(); + create(); + } + + void xml_document::reset(const xml_document& proto) + { + reset(); + + for (xml_node cur = proto.first_child(); cur; cur = cur.next_sibling()) + append_copy(cur); + } + + void xml_document::create() + { + // initialize sentinel page + STATIC_ASSERT(offsetof(xml_memory_page, data) + sizeof(xml_document_struct) + xml_memory_page_alignment <= sizeof(_memory)); + + // align upwards to page boundary + void* page_memory = reinterpret_cast((reinterpret_cast(_memory) + (xml_memory_page_alignment - 1)) & ~(xml_memory_page_alignment - 1)); + + // prepare page structure + xml_memory_page* page = xml_memory_page::construct(page_memory); + + page->busy_size = xml_memory_page_size; + + // allocate new root + _root = new (page->data) xml_document_struct(page); + _root->prev_sibling_c = _root; + + // setup sentinel page + page->allocator = static_cast(_root); + } + + void xml_document::destroy() + { + // destroy static storage + if (_buffer) + { + global_deallocate(_buffer); + _buffer = 0; + } + + // destroy dynamic storage, leave sentinel page (it's in static memory) + if (_root) + { + xml_memory_page* root_page = reinterpret_cast(_root->header & xml_memory_page_pointer_mask); + assert(root_page && !root_page->prev && !root_page->memory); + + // destroy all pages + for (xml_memory_page* page = root_page->next; page; ) + { + xml_memory_page* next = page->next; + + xml_allocator::deallocate_page(page); + + page = next; + } + + // cleanup root page + root_page->allocator = 0; + root_page->next = 0; + root_page->busy_size = root_page->freed_size = 0; + + _root = 0; + } + } + +#ifndef PUGIXML_NO_STL + xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options, xml_encoding encoding) + { + reset(); + + return load_stream_impl(*this, stream, options, encoding); + } + + xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options) + { + reset(); + + return load_stream_impl(*this, stream, options, encoding_wchar); + } +#endif + + xml_parse_result xml_document::load(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, strlength(contents) * sizeof(char_t), options, encoding); + } + + xml_parse_result xml_document::load_file(const char* path, unsigned int options, xml_encoding encoding) + { + reset(); + + FILE* file = fopen(path, "rb"); + + return load_file_impl(*this, file, options, encoding); + } + + xml_parse_result xml_document::load_file(const wchar_t* path, unsigned int options, xml_encoding encoding) + { + reset(); + + FILE* file = open_file_wide(path, L"rb"); + + return load_file_impl(*this, file, options, encoding); + } + + xml_parse_result xml_document::load_buffer_impl(void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own) + { + reset(); + + // check input buffer + assert(contents || size == 0); + + // get actual encoding + xml_encoding buffer_encoding = get_buffer_encoding(encoding, contents, size); + + // get private buffer + char_t* buffer = 0; + size_t length = 0; + + if (!convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return make_parse_result(status_out_of_memory); + + // delete original buffer if we performed a conversion + if (own && buffer != contents && contents) global_deallocate(contents); + + // parse + xml_parse_result res = xml_parser::parse(buffer, length, _root, options); + + // remember encoding + res.encoding = buffer_encoding; + + // grab onto buffer if it's our buffer, user is responsible for deallocating contens himself + if (own || buffer != contents) _buffer = buffer; + + return res; + } + + xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) + { + return load_buffer_impl(const_cast(contents), size, options, encoding, false, false); + } + + xml_parse_result xml_document::load_buffer_inplace(void* contents, size_t size, unsigned int options, xml_encoding encoding) + { + return load_buffer_impl(contents, size, options, encoding, true, false); + } + + xml_parse_result xml_document::load_buffer_inplace_own(void* contents, size_t size, unsigned int options, xml_encoding encoding) + { + return load_buffer_impl(contents, size, options, encoding, true, true); + } + + void xml_document::save(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding) const + { + if (flags & format_write_bom) write_bom(writer, get_write_encoding(encoding)); + + xml_buffered_writer buffered_writer(writer, encoding); + + if (!(flags & format_no_declaration) && !has_declaration(*this)) + { + buffered_writer.write(PUGIXML_TEXT("")); + if (!(flags & format_raw)) buffered_writer.write('\n'); + } + + node_output(buffered_writer, *this, indent, flags, 0); + } + +#ifndef PUGIXML_NO_STL + 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); + } + + 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 + + bool xml_document::save_file(const char* path, const char_t* indent, unsigned int flags, xml_encoding encoding) const + { + FILE* file = fopen(path, "wb"); + if (!file) return false; + + xml_writer_file writer(file); + save(writer, indent, flags, encoding); + + fclose(file); + + return true; + } + + bool xml_document::save_file(const wchar_t* path, const char_t* indent, unsigned int flags, xml_encoding encoding) const + { + FILE* file = open_file_wide(path, L"wb"); + if (!file) return false; + + xml_writer_file writer(file); + save(writer, indent, flags, encoding); + + fclose(file); + + return true; + } + + xml_node xml_document::document_element() const + { + for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) + if ((i->header & xml_memory_page_type_mask) + 1 == node_element) + return xml_node(i); + + return xml_node(); + } + +#ifndef PUGIXML_NO_STL + std::string PUGIXML_FUNCTION as_utf8(const wchar_t* str) + { + assert(str); + + return as_utf8_impl(str, wcslen(str)); + } + + std::string PUGIXML_FUNCTION as_utf8(const std::wstring& str) + { + return as_utf8_impl(str.c_str(), str.size()); + } + + std::wstring PUGIXML_FUNCTION as_wide(const char* str) + { + assert(str); + + return as_wide_impl(str, strlen(str)); + } + + std::wstring PUGIXML_FUNCTION as_wide(const std::string& str) + { + return as_wide_impl(str.c_str(), str.size()); + } +#endif + + void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate) + { + global_allocate = allocate; + global_deallocate = deallocate; + } + + allocation_function PUGIXML_FUNCTION get_memory_allocation_function() + { + return global_allocate; + } + + deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function() + { + return global_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) + std::bidirectional_iterator_tag _Iter_cat(const xml_node_iterator&) + { + return std::bidirectional_iterator_tag(); + } + + std::bidirectional_iterator_tag _Iter_cat(const xml_attribute_iterator&) + { + return std::bidirectional_iterator_tag(); + } +} +#endif + +#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) +namespace std +{ + // Workarounds for (non-standard) iterator category detection + std::bidirectional_iterator_tag __iterator_category(const xml_node_iterator&) + { + return std::bidirectional_iterator_tag(); + } + + std::bidirectional_iterator_tag __iterator_category(const xml_attribute_iterator&) + { + return std::bidirectional_iterator_tag(); + } +} +#endif + +#ifndef PUGIXML_NO_XPATH + +// STL replacements +namespace +{ + 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 void swap(T& lhs, T& rhs) + { + T temp = lhs; + lhs = rhs; + rhs = temp; + } + + template 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 void reverse(I begin, I end) + { + while (begin + 1 < end) swap(*begin++, *--end); + } + + template I unique(I begin, I end) + { + // fast skip head + while (begin + 1 < end && *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 void copy_backwards(I begin, I end, I target) + { + while (begin != end) *--target = *--end; + } + + template void insertion_sort(I begin, I end, const Pred& pred, T*) + { + assert(begin != end); + + for (I it = begin + 1; it != end; ++it) + { + T val = *it; + + if (pred(val, *begin)) + { + // move to front + copy_backwards(begin, it, it + 1); + *begin = val; + } + else + { + I hole = it; + + // move hole backwards + while (pred(val, *(hole - 1))) + { + *hole = *(hole - 1); + hole--; + } + + // fill hole with element + *hole = val; + } + } + } + + // std variant for elements with == + template void partition(I begin, I middle, I end, const Pred& pred, I* out_eqbeg, I* out_eqend) + { + I eqbeg = middle, eqend = middle + 1; + + // expand equal range + while (eqbeg != begin && *(eqbeg - 1) == *eqbeg) --eqbeg; + while (eqend != end && *eqend == *eqbeg) ++eqend; + + // process outer elements + I ltend = eqbeg, gtbeg = eqend; + + for (;;) + { + // find the element from the right side that belongs to the left one + for (; gtbeg != end; ++gtbeg) + if (!pred(*eqbeg, *gtbeg)) + { + if (*gtbeg == *eqbeg) swap(*gtbeg, *eqend++); + else break; + } + + // find the element from the left side that belongs to the right one + for (; ltend != begin; --ltend) + if (!pred(*(ltend - 1), *eqbeg)) + { + if (*eqbeg == *(ltend - 1)) swap(*(ltend - 1), *--eqbeg); + else break; + } + + // scanned all elements + if (gtbeg == end && ltend == begin) + { + *out_eqbeg = eqbeg; + *out_eqend = eqend; + return; + } + + // make room for elements by moving equal area + if (gtbeg == end) + { + if (--ltend != --eqbeg) swap(*ltend, *eqbeg); + swap(*eqbeg, *--eqend); + } + else if (ltend == begin) + { + if (eqend != gtbeg) swap(*eqbeg, *eqend); + ++eqend; + swap(*gtbeg++, *eqbeg++); + } + else swap(*gtbeg++, *--ltend); + } + } + + template void 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); + } + + template void median(I first, I middle, I last, const Pred& pred) + { + if (last - first <= 40) + { + // median of three for small chunks + median3(first, middle, last, pred); + } + else + { + // median of nine + size_t step = (last - first + 1) / 8; + + median3(first, first + step, first + 2 * step, pred); + median3(middle - step, middle, middle + step, pred); + median3(last - 2 * step, last - step, last, pred); + median3(first + step, middle, last - step, pred); + } + } + + template void sort(I begin, I end, const Pred& pred) + { + // sort large chunks + while (end - begin > 32) + { + // find median element + I middle = begin + (end - begin) / 2; + median(begin, middle, end - 1, pred); + + // partition in three chunks (< = >) + I eqbeg, eqend; + partition(begin, middle, end, 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 + if (begin != end) insertion_sort(begin, end, pred, &*begin); + } +} + +// Allocator used for AST and evaluation stacks +namespace +{ + struct xpath_memory_block + { + xpath_memory_block* next; + + char data[4096]; + }; + + class xpath_allocator + { + xpath_memory_block* _root; + size_t _root_size; + + public: +#ifdef PUGIXML_NO_EXCEPTIONS + jmp_buf* error_handler; +#endif + + xpath_allocator(xpath_memory_block* root, size_t root_size = 0): _root(root), _root_size(root_size) + { +#ifdef PUGIXML_NO_EXCEPTIONS + error_handler = 0; +#endif + } + + void* allocate_nothrow(size_t size) + { + const size_t block_capacity = sizeof(_root->data); + + // align size so that we're able to store pointers in subsequent blocks + size = (size + sizeof(void*) - 1) & ~(sizeof(void*) - 1); + + if (_root_size + size <= block_capacity) + { + void* buf = _root->data + _root_size; + _root_size += size; + return buf; + } + else + { + size_t block_data_size = (size > block_capacity) ? size : block_capacity; + size_t block_size = block_data_size + offsetof(xpath_memory_block, data); + + xpath_memory_block* block = static_cast(global_allocate(block_size)); + if (!block) return 0; + + block->next = _root; + + _root = block; + _root_size = size; + + return block->data; + } + } + + void* allocate(size_t size) + { + void* result = allocate_nothrow(size); + + if (!result) + { +#ifdef PUGIXML_NO_EXCEPTIONS + assert(error_handler); + longjmp(*error_handler, 1); +#else + throw std::bad_alloc(); +#endif + } + + return result; + } + + void* reallocate(void* ptr, size_t old_size, size_t new_size) + { + // align size so that we're able to store pointers in subsequent blocks + old_size = (old_size + sizeof(void*) - 1) & ~(sizeof(void*) - 1); + new_size = (new_size + sizeof(void*) - 1) & ~(sizeof(void*) - 1); + + // we can only reallocate the last object + assert(ptr == 0 || static_cast(ptr) + old_size == _root->data + _root_size); + + // adjust root size so that we have not allocated the object at all + bool only_object = (_root_size == old_size); + + if (ptr) _root_size -= old_size; + + // allocate a new version (this will obviously reuse the memory if possible) + void* result = allocate(new_size); + assert(result); + + // we have a new block + if (result != ptr && ptr) + { + // copy old data + assert(new_size > old_size); + memcpy(result, ptr, old_size); + + // free the previous page if it had no other objects + if (only_object) + { + assert(_root->data == result); + assert(_root->next); + + xpath_memory_block* next = _root->next->next; + + if (next) + { + // deallocate the whole page, unless it was the first one + global_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; + + global_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; + + global_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; + +#ifdef PUGIXML_NO_EXCEPTIONS + jmp_buf error_handler; +#endif + + xpath_stack_data(): result(blocks + 0), temp(blocks + 1) + { + blocks[0].next = blocks[1].next = 0; + + stack.result = &result; + stack.temp = &temp; + +#ifdef PUGIXML_NO_EXCEPTIONS + result.error_handler = temp.error_handler = &error_handler; +#endif + } + + ~xpath_stack_data() + { + result.release(); + temp.release(); + } + }; +} + +// String class +namespace +{ + class xpath_string + { + const char_t* _buffer; + bool _uses_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))); + assert(result); + + memcpy(result, string, length * sizeof(char_t)); + result[length] = 0; + + return result; + } + + static char_t* duplicate_string(const char_t* string, xpath_allocator* alloc) + { + return duplicate_string(string, strlength(string), alloc); + } + + public: + xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false) + { + } + + explicit xpath_string(const char_t* str, xpath_allocator* alloc) + { + bool empty = (*str == 0); + + _buffer = empty ? PUGIXML_TEXT("") : duplicate_string(str, alloc); + _uses_heap = !empty; + } + + explicit xpath_string(const char_t* str, bool use_heap): _buffer(str), _uses_heap(use_heap) + { + } + + xpath_string(const char_t* begin, const char_t* end, xpath_allocator* alloc) + { + assert(begin <= end); + + bool empty = (begin == end); + + _buffer = empty ? PUGIXML_TEXT("") : duplicate_string(begin, static_cast(end - begin), alloc); + _uses_heap = !empty; + } + + 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 = strlength(_buffer); + size_t source_length = strlength(o._buffer); + size_t 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), (length + 1) * sizeof(char_t))); + assert(result); + + // 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[length] = 0; + + // finalize + _buffer = result; + _uses_heap = true; + } + } + + const char_t* c_str() const + { + return _buffer; + } + + size_t length() const + { + return strlength(_buffer); + } + + char_t* data(xpath_allocator* alloc) + { + // make private heap copy + if (!_uses_heap) + { + _buffer = duplicate_string(_buffer, alloc); + _uses_heap = true; + } + + 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; + } + }; + + xpath_string xpath_string_const(const char_t* str) + { + return xpath_string(str, false); + } +} + +namespace +{ + bool starts_with(const char_t* string, const char_t* pattern) + { + while (*pattern && *string == *pattern) + { + string++; + pattern++; + } + + return *pattern == 0; + } + + 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 + } + + 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 + char_t tolower_ascii(char_t ch) + { + return static_cast(ch - 'A') < 26 ? static_cast(ch | ' ') : ch; + } + + xpath_string string_value(const xpath_node& na, xpath_allocator* alloc) + { + if (na.attribute()) + return xpath_string_const(na.attribute().value()); + else + { + const xml_node& n = na.node(); + + switch (n.type()) + { + case node_pcdata: + case node_cdata: + case node_comment: + case node_pi: + return xpath_string_const(n.value()); + + case node_document: + case node_element: + { + xpath_string result; + + xml_node cur = n.first_child(); + + while (cur && cur != n) + { + if (cur.type() == node_pcdata || cur.type() == node_cdata) + result.append(xpath_string_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(); + } + } + } + + unsigned int node_height(xml_node n) + { + unsigned int result = 0; + + while (n) + { + ++result; + n = n.parent(); + } + + return result; + } + + bool node_is_before(xml_node ln, unsigned int lh, xml_node rn, unsigned int rh) + { + // normalize heights + for (unsigned int i = rh; i < lh; i++) ln = ln.parent(); + for (unsigned int j = lh; j < rh; j++) rn = rn.parent(); + + // one node is the ancestor of the other + if (ln == rn) return lh < rh; + + // find common ancestor + while (ln.parent() != rn.parent()) + { + ln = ln.parent(); + rn = 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 + for (; ln; ln = ln.next_sibling()) + if (ln == rn) + return true; + + return false; + } + + bool node_is_ancestor(xml_node parent, xml_node node) + { + while (node && node != parent) node = node.parent(); + + return parent && node == parent; + } + + const void* document_order(const xpath_node& xnode) + { + xml_node_struct* node = xnode.node().internal_object(); + + if (node) + { + if (node->name && (node->header & xml_memory_page_name_allocated_mask) == 0) return node->name; + if (node->value && (node->header & xml_memory_page_value_allocated_mask) == 0) return node->value; + return 0; + } + + xml_attribute_struct* attr = xnode.attribute().internal_object(); + + if (attr) + { + if ((attr->header & xml_memory_page_name_allocated_mask) == 0) return attr->name; + if ((attr->header & xml_memory_page_value_allocated_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_order(lhs); + const void* ro = document_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; + + unsigned int lh = node_height(ln); + unsigned int rh = node_height(rn); + + return node_is_before(ln, lh, rn, rh); + } + }; + + struct duplicate_comparator + { + bool operator()(const xpath_node& lhs, const xpath_node& rhs) const + { + if (lhs.attribute()) return rhs.attribute() ? lhs.attribute() < rhs.attribute() : true; + else return rhs.attribute() ? false : lhs.node() < rhs.node(); + } + }; + + double gen_nan() + { +#if defined(__STDC_IEC_559__) || ((FLT_RADIX - 0 == 2) && (FLT_MAX_EXP - 0 == 128) && (FLT_MANT_DIG - 0 == 24)) + union { float f; int32_t i; } u[sizeof(float) == sizeof(int32_t) ? 1 : -1]; + u[0].i = 0x7fc00000; + return u[0].f; +#else + // fallback + const volatile double zero = 0.0; + return zero / zero; +#endif + } + + bool is_nan(double value) + { +#if defined(_MSC_VER) || 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 + } + + const char_t* convert_number_to_string_special(double value) + { +#if defined(_MSC_VER) || defined(__BORLANDC__) + if (_finite(value)) return (value == 0) ? PUGIXML_TEXT("0") : 0; + if (_isnan(value)) return PUGIXML_TEXT("NaN"); + return PUGIXML_TEXT("-Infinity") + (value > 0); +#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 PUGIXML_TEXT("-Infinity") + (value > 0); + + 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 PUGIXML_TEXT("-Infinity") + (value > 0); + return 0; +#endif + } + + bool convert_number_to_boolean(double value) + { + return (value != 0 && !is_nan(value)); + } + + 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(_MSC_VER) && _MSC_VER >= 1400 + void convert_number_to_mantissa_exponent(double value, char* buffer, size_t buffer_size, char** out_mantissa, int* out_exponent) + { + // get base values + int sign, exponent; + _ecvt_s(buffer, buffer_size, value, DBL_DIG + 1, &exponent, &sign); + + // truncate redundant zeros + truncate_zeros(buffer, buffer + strlen(buffer)); + + // fill results + *out_mantissa = buffer; + *out_exponent = exponent; + } +#else + void convert_number_to_mantissa_exponent(double value, char* buffer, size_t buffer_size, char** out_mantissa, int* out_exponent) + { + // get a scientific notation value with IEEE DBL_DIG decimals + sprintf(buffer, "%.*e", DBL_DIG, value); + assert(strlen(buffer) < buffer_size); + (void)!buffer_size; + + // 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 + + 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_const(special); + + // get mantissa + exponent form + char mantissa_buffer[64]; + + char* mantissa; + int exponent; + convert_number_to_mantissa_exponent(value, mantissa_buffer, sizeof(mantissa_buffer), &mantissa, &exponent); + + // make the number! + char_t result[512]; + char_t* s = result; + + // sign + if (value < 0) *s++ = '-'; + + // integer part + if (exponent <= 0) + { + *s++ = '0'; + } + else + { + while (exponent > 0) + { + assert(*mantissa == 0 || (unsigned)(*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((unsigned)(*mantissa - '0') <= 9); + *s++ = *mantissa++; + } + } + + // zero-terminate + assert(s < result + sizeof(result) / sizeof(result[0])); + *s = 0; + + return xpath_string(result, alloc); + } + + bool check_string_to_number_format(const char_t* string) + { + // parse leading whitespace + while (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 (!IS_CHARTYPEX(string[0], ctx_digit) && (string[0] != '.' || !IS_CHARTYPEX(string[1], ctx_digit))) return false; + + // parse integer part + while (IS_CHARTYPEX(*string, ctx_digit)) ++string; + + // parse decimal part + if (*string == '.') + { + ++string; + + while (IS_CHARTYPEX(*string, ctx_digit)) ++string; + } + + // parse trailing whitespace + while (IS_CHARTYPE(*string, ct_space)) ++string; + + return *string == 0; + } + + 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 atof(string); +#endif + } + + bool convert_string_to_number(const char_t* begin, const char_t* end, double* out_result) + { + char_t buffer[32]; + + 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(global_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) global_deallocate(scratch); + + return true; + } + + double round_nearest(double value) + { + return floor(value + 0.5); + } + + 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); + } + + const char_t* qualified_name(const xpath_node& node) + { + return node.attribute() ? node.attribute().name() : node.node().name(); + } + + 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()(const 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; + } + }; + + const char_t* namespace_uri(const 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(""); + } + + const char_t* namespace_uri(const xml_attribute& attr, const 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(""); + } + + const char_t* namespace_uri(const xpath_node& node) + { + return node.attribute() ? namespace_uri(node.attribute(), node.parent()) : namespace_uri(node.node()); + } + + void normalize_space(char_t* buffer) + { + char_t* write = buffer; + + for (char_t* it = buffer; *it; ) + { + char_t ch = *it++; + + if (IS_CHARTYPE(ch, ct_space)) + { + // replace whitespace sequence with single space + while (IS_CHARTYPE(*it, ct_space)) it++; + + // avoid leading spaces + if (write != buffer) *write++ = ' '; + } + else *write++ = ch; + } + + // remove trailing space + if (write != buffer && IS_CHARTYPE(write[-1], ct_space)) write--; + + // zero-terminate + *write = 0; + } + + void translate(char_t* buffer, const char_t* from, const char_t* to) + { + size_t to_length = strlength(to); + + char_t* write = buffer; + + while (*buffer) + { + 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; + } + + struct xpath_variable_boolean: xpath_variable + { + xpath_variable_boolean(): value(false) + { + } + + bool value; + char_t name[1]; + }; + + struct xpath_variable_number: xpath_variable + { + xpath_variable_number(): value(0) + { + } + + double value; + char_t name[1]; + }; + + struct xpath_variable_string: xpath_variable + { + xpath_variable_string(): value(0) + { + } + + ~xpath_variable_string() + { + if (value) global_deallocate(value); + } + + char_t* value; + char_t name[1]; + }; + + struct xpath_variable_node_set: xpath_variable + { + xpath_node_set value; + char_t name[1]; + }; + + const xpath_node_set dummy_node_set; + + 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 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 = global_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; + } + + 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 void delete_xpath_variable(T* var) + { + var->~T(); + global_deallocate(var); + } + + 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(!"Invalid variable type"); + } + } + + xpath_variable* get_variable(xpath_variable_set* set, const char_t* begin, const char_t* end) + { + char_t buffer[32]; + + 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(global_allocate((length + 1) * sizeof(char_t))); + if (!scratch) return 0; + } + + // copy string to zero-terminated buffer and perform lookup + memcpy(scratch, begin, length * sizeof(char_t)); + scratch[length] = 0; + + xpath_variable* result = set->get(scratch); + + // free dummy buffer + if (scratch != buffer) global_deallocate(scratch); + + return result; + } +} + +// Internal node set class +namespace +{ + 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) + { + sort(begin, end, document_order_comparator()); + + type = xpath_node_set::type_sorted; + } + + if (type != order) reverse(begin, end); + + return order; + } + + 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(!"Invalid node set type"); + 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(const xpath_node& node, xpath_allocator* alloc) + { + if (_end == _eos) + { + 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))); + assert(data); + + // finalize + _begin = data; + _end = data + capacity; + _eos = data + new_capacity; + } + + *_end++ = node; + } + + void append(const xpath_node* begin, const xpath_node* end, xpath_allocator* alloc) + { + 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))); + assert(data); + + // 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() + { + if (_type == xpath_node_set::type_unsorted) + sort(_begin, _end, duplicate_comparator()); + + _end = unique(_begin, _end); + } + + xpath_node_set::type_t type() const + { + return _type; + } + + void set_type(xpath_node_set::type_t type) + { + _type = type; + } + }; +} + +namespace +{ + 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 (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 (IS_CHARTYPEX(*cur, ctx_start_symbol)) + { + _cur_lexeme_contents.begin = cur; + + while (IS_CHARTYPEX(*cur, ctx_symbol)) cur++; + + if (cur[0] == ':' && IS_CHARTYPEX(cur[1], ctx_symbol)) // qname + { + cur++; // : + + while (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 (IS_CHARTYPEX(*(cur+1), ctx_digit)) + { + _cur_lexeme_contents.begin = cur; // . + + ++cur; + + while (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 (IS_CHARTYPEX(*cur, ctx_digit)) + { + _cur_lexeme_contents.begin = cur; + + while (IS_CHARTYPEX(*cur, ctx_digit)) cur++; + + if (*cur == '.') + { + cur++; + + while (IS_CHARTYPEX(*cur, ctx_digit)) cur++; + } + + _cur_lexeme_contents.end = cur; + + _cur_lexeme = lex_number; + } + else if (IS_CHARTYPEX(*cur, ctx_start_symbol)) + { + _cur_lexeme_contents.begin = cur; + + while (IS_CHARTYPEX(*cur, ctx_symbol)) cur++; + + if (cur[0] == ':') + { + if (cur[1] == '*') // namespace test ncname:* + { + cur += 2; // :* + } + else if (IS_CHARTYPEX(cur[1], ctx_symbol)) // namespace test qname + { + cur++; // : + + while (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_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_filter_posinv, // select * from left where right; proximity position invariant + 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 + }; + + 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 + }; + + 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 / ast_predicate + char _axis; + 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; + } _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); + xpath_node_set_raw rs = rhs->eval_node_set(c, stack); + + 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); + + 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); + + 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(!"Wrong types"); + return false; + } + + 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); + xpath_node_set_raw rs = rhs->eval_node_set(c, stack); + + 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); + + 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); + 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(!"Wrong types"); + return false; + } + } + + void apply_predicate(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack) + { + assert(ns.size() >= first); + + 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->rettype() == xpath_type_number) + { + if (expr->eval_number(c, stack) == i) + *last++ = *it; + } + else if (expr->eval_boolean(c, stack)) + *last++ = *it; + } + + ns.truncate(last); + } + + void apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack) + { + if (ns.size() == first) return; + + for (xpath_ast_node* pred = _right; pred; pred = pred->_next) + { + apply_predicate(ns, first, pred->_left, stack); + } + } + + void step_push(xpath_node_set_raw& ns, const xml_attribute& a, const xml_node& parent, xpath_allocator* alloc) + { + if (!a) return; + + const char_t* name = a.name(); + + // There are no attribute nodes corresponding to attributes that declare namespaces + // That is, "xmlns:..." or "xmlns" + if (starts_with(name, PUGIXML_TEXT("xmlns")) && (name[5] == 0 || name[5] == ':')) return; + + switch (_test) + { + case nodetest_name: + if (strequal(name, _data.nodetest)) ns.push_back(xpath_node(a, parent), alloc); + break; + + case nodetest_type_node: + case nodetest_all: + ns.push_back(xpath_node(a, parent), alloc); + break; + + case nodetest_all_in_namespace: + if (starts_with(name, _data.nodetest)) + ns.push_back(xpath_node(a, parent), alloc); + break; + + default: + ; + } + } + + void step_push(xpath_node_set_raw& ns, const xml_node& n, xpath_allocator* alloc) + { + if (!n) return; + + switch (_test) + { + case nodetest_name: + if (n.type() == node_element && strequal(n.name(), _data.nodetest)) ns.push_back(n, alloc); + break; + + case nodetest_type_node: + ns.push_back(n, alloc); + break; + + case nodetest_type_comment: + if (n.type() == node_comment) + ns.push_back(n, alloc); + break; + + case nodetest_type_text: + if (n.type() == node_pcdata || n.type() == node_cdata) + ns.push_back(n, alloc); + break; + + case nodetest_type_pi: + if (n.type() == node_pi) + ns.push_back(n, alloc); + break; + + case nodetest_pi: + if (n.type() == node_pi && strequal(n.name(), _data.nodetest)) + ns.push_back(n, alloc); + break; + + case nodetest_all: + if (n.type() == node_element) + ns.push_back(n, alloc); + break; + + case nodetest_all_in_namespace: + if (n.type() == node_element && starts_with(n.name(), _data.nodetest)) + ns.push_back(n, alloc); + break; + + default: + assert(!"Unknown axis"); + } + } + + template void step_fill(xpath_node_set_raw& ns, const xml_node& n, xpath_allocator* alloc, T) + { + const axis_t axis = T::axis; + + switch (axis) + { + case axis_attribute: + { + for (xml_attribute a = n.first_attribute(); a; a = a.next_attribute()) + step_push(ns, a, n, alloc); + + break; + } + + case axis_child: + { + for (xml_node c = n.first_child(); c; c = c.next_sibling()) + step_push(ns, c, alloc); + + break; + } + + case axis_descendant: + case axis_descendant_or_self: + { + if (axis == axis_descendant_or_self) + step_push(ns, n, alloc); + + xml_node cur = n.first_child(); + + while (cur && cur != n) + { + step_push(ns, cur, 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(); + } + } + + break; + } + + case axis_following_sibling: + { + for (xml_node c = n.next_sibling(); c; c = c.next_sibling()) + step_push(ns, c, alloc); + + break; + } + + case axis_preceding_sibling: + { + for (xml_node c = n.previous_sibling(); c; c = c.previous_sibling()) + step_push(ns, c, alloc); + + break; + } + + case axis_following: + { + xml_node cur = n; + + // exit from this node so that we don't include descendants + while (cur && !cur.next_sibling()) cur = cur.parent(); + cur = cur.next_sibling(); + + for (;;) + { + step_push(ns, cur, alloc); + + if (cur.first_child()) + cur = cur.first_child(); + else if (cur.next_sibling()) + cur = cur.next_sibling(); + else + { + while (cur && !cur.next_sibling()) cur = cur.parent(); + cur = cur.next_sibling(); + + if (!cur) break; + } + } + + break; + } + + case axis_preceding: + { + xml_node cur = n; + + while (cur && !cur.previous_sibling()) cur = cur.parent(); + cur = cur.previous_sibling(); + + for (;;) + { + if (cur.last_child()) + cur = cur.last_child(); + else + { + // leaf node, can't be ancestor + step_push(ns, cur, alloc); + + if (cur.previous_sibling()) + cur = cur.previous_sibling(); + else + { + do + { + cur = cur.parent(); + if (!cur) break; + + if (!node_is_ancestor(cur, n)) step_push(ns, cur, alloc); + } + while (!cur.previous_sibling()); + + cur = cur.previous_sibling(); + + if (!cur) break; + } + } + } + + break; + } + + case axis_ancestor: + case axis_ancestor_or_self: + { + if (axis == axis_ancestor_or_self) + step_push(ns, n, alloc); + + xml_node cur = n.parent(); + + while (cur) + { + step_push(ns, cur, alloc); + + 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(!"Unimplemented axis"); + } + } + + template void step_fill(xpath_node_set_raw& ns, const xml_attribute& a, const xml_node& p, xpath_allocator* alloc, 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 + step_push(ns, a, p, alloc); + + xml_node cur = p; + + while (cur) + { + step_push(ns, cur, alloc); + + 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 cur = p; + + for (;;) + { + if (cur.first_child()) + cur = cur.first_child(); + else if (cur.next_sibling()) + cur = cur.next_sibling(); + else + { + while (cur && !cur.next_sibling()) cur = cur.parent(); + cur = cur.next_sibling(); + + if (!cur) break; + } + + step_push(ns, cur, alloc); + } + + 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, v); + break; + } + + default: + assert(!"Unimplemented axis"); + } + } + + template xpath_node_set_raw step_do(const xpath_context& c, const xpath_stack& stack, T v) + { + const axis_t axis = T::axis; + bool 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); + + xpath_node_set_raw ns; + ns.set_type((axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling) ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted); + + if (_left) + { + xpath_node_set_raw s = _left->eval_node_set(c, stack); + + // 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); + + if (it->node()) + step_fill(ns, it->node(), stack.result, v); + else if (attributes) + step_fill(ns, it->attribute(), it->parent(), stack.result, v); + + apply_predicates(ns, size, stack); + } + } + else + { + if (c.n.node()) + step_fill(ns, c.n.node(), stack.result, v); + else if (attributes) + step_fill(ns, c.n.attribute(), c.n.parent(), stack.result, v); + + apply_predicates(ns, 0, stack); + } + + // 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(); + + return ns; + } + + public: + xpath_ast_node(ast_type_t type, xpath_value_type rettype, const char_t* value): + _type((char)type), _rettype((char)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((char)type), _rettype((char)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((char)type), _rettype((char)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((char)type), _rettype((char)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((char)type), _rettype(xpath_type_node_set), _axis((char)axis), _test((char)test), _left(left), _right(0), _next(0) + { + _data.nodetest = contents; + } + + 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_variable: + { + assert(_rettype == _data.variable->type()); + + if (_rettype == xpath_type_boolean) + return _data.variable->get_boolean(); + + // fallthrough to type conversion + } + + 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).empty(); + } + + default: + assert(!"Wrong expression for return type boolean"); + 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 (double)c.size; + + case ast_func_position: + return (double)c.position; + + case ast_func_count: + { + xpath_allocator_capture cr(stack.result); + + return (double)_left->eval_node_set(c, stack).size(); + } + + case ast_func_string_length_0: + { + xpath_allocator_capture cr(stack.result); + + return (double)string_value(c.n, stack.result).length(); + } + + case ast_func_string_length_1: + { + xpath_allocator_capture cr(stack.result); + + return (double)_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); + + 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 to type conversion + } + + 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(!"Wrong expression for return type number"); + 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++; + + // gather all strings + xpath_string static_buffer[4]; + xpath_string* buffer = static_buffer; + + // allocate on-heap for large concats + if (count > sizeof(static_buffer) / sizeof(static_buffer[0])) + { + buffer = static_cast(stack.temp->allocate(count * sizeof(xpath_string))); + assert(buffer); + } + + // 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))); + assert(result); + + 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(result, true); + } + + xpath_string eval_string(const xpath_context& c, const xpath_stack& stack) + { + switch (_type) + { + case ast_string_constant: + return xpath_string_const(_data.string); + + case ast_func_local_name_0: + { + xpath_node na = c.n; + + return xpath_string_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); + xpath_node na = ns.first(); + + return xpath_string_const(local_name(na)); + } + + case ast_func_name_0: + { + xpath_node na = c.n; + + return xpath_string_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); + xpath_node na = ns.first(); + + return xpath_string_const(qualified_name(na)); + } + + case ast_func_namespace_uri_0: + { + xpath_node na = c.n; + + return xpath_string_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); + xpath_node na = ns.first(); + + return xpath_string_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(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* result = pos + p.length(); + + return s.uses_heap() ? xpath_string(result, stack.result) : xpath_string_const(result); + } + + 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 : (size_t)first; + assert(1 <= pos && pos <= s_length + 1); + + const char_t* rbegin = s.c_str() + (pos - 1); + + return s.uses_heap() ? xpath_string(rbegin, stack.result) : xpath_string_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 : (size_t)first; + size_t end = last >= s_length + 1 ? s_length + 1 : (size_t)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_const(rbegin) : xpath_string(rbegin, rend, stack.result); + } + + case ast_func_normalize_space_0: + { + xpath_string s = string_value(c.n, stack.result); + + normalize_space(s.data(stack.result)); + + return s; + } + + case ast_func_normalize_space_1: + { + xpath_string s = _left->eval_string(c, stack); + + normalize_space(s.data(stack.result)); + + return s; + } + + 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); + + translate(s.data(stack.result), from.c_str(), to.c_str()); + + return s; + } + + case ast_variable: + { + assert(_rettype == _data.variable->type()); + + if (_rettype == xpath_type_string) + return xpath_string_const(_data.variable->get_string()); + + // fallthrough to type conversion + } + + default: + { + switch (_rettype) + { + case xpath_type_boolean: + return xpath_string_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); + return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result); + } + + default: + assert(!"Wrong expression for return type string"); + return xpath_string(); + } + } + } + } + + xpath_node_set_raw eval_node_set(const xpath_context& c, const xpath_stack& stack) + { + 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, swapped_stack); + xpath_node_set_raw rs = _right->eval_node_set(c, stack); + + // we can optimize merging two sorted sets, but this is a very rare operation, so don't bother + rs.set_type(xpath_node_set::type_unsorted); + + rs.append(ls.begin(), ls.end(), stack.result); + rs.remove_duplicates(); + + return rs; + } + + case ast_filter: + case ast_filter_posinv: + { + xpath_node_set_raw set = _left->eval_node_set(c, stack); + + // either expression is a number or it contains position() call; sort by document order + if (_type == ast_filter) set.sort_do(); + + apply_predicate(set, 0, _right, stack); + + return set; + } + + case ast_func_id: + return xpath_node_set_raw(); + + case ast_step: + { + switch (_axis) + { + case axis_ancestor: + return step_do(c, stack, axis_to_type()); + + case axis_ancestor_or_self: + return step_do(c, stack, axis_to_type()); + + case axis_attribute: + return step_do(c, stack, axis_to_type()); + + case axis_child: + return step_do(c, stack, axis_to_type()); + + case axis_descendant: + return step_do(c, stack, axis_to_type()); + + case axis_descendant_or_self: + return step_do(c, stack, axis_to_type()); + + case axis_following: + return step_do(c, stack, axis_to_type()); + + case axis_following_sibling: + return step_do(c, stack, axis_to_type()); + + case axis_namespace: + // namespaced axis is not supported + return xpath_node_set_raw(); + + case axis_parent: + return step_do(c, stack, axis_to_type()); + + case axis_preceding: + return step_do(c, stack, axis_to_type()); + + case axis_preceding_sibling: + return step_do(c, stack, axis_to_type()); + + case axis_self: + return step_do(c, stack, axis_to_type()); + } + } + + 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 to type conversion + } + + default: + assert(!"Wrong expression for return type node set"); + return xpath_node_set_raw(); + } + } + + bool is_posinv() + { + switch (_type) + { + case ast_func_position: + 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: + case ast_filter_posinv: + return true; + + default: + if (_left && !_left->is_posinv()) return false; + + for (xpath_ast_node* n = _right; n; n = n->_next) + if (!n->is_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; + +#ifdef PUGIXML_NO_EXCEPTIONS + jmp_buf _error_handler; +#endif + + void throw_error(const char* message) + { + _result->error = message; + _result->offset = _lexer.current_pos() - _query; + +#ifdef PUGIXML_NO_EXCEPTIONS + longjmp(_error_handler, 1); +#else + throw xpath_exception(*_result); +#endif + } + + void throw_error_oom() + { +#ifdef PUGIXML_NO_EXCEPTIONS + throw_error("Out of memory"); +#else + throw std::bad_alloc(); +#endif + } + + void* alloc_node() + { + void* result = _alloc->allocate_nothrow(sizeof(xpath_ast_node)); + + if (!result) throw_error_oom(); + + return result; + } + + const char_t* alloc_string(const xpath_lexer_string& value) + { + if (value.begin) + { + size_t length = static_cast(value.end - value.begin); + + char_t* c = static_cast(_alloc->allocate_nothrow((length + 1) * sizeof(char_t))); + if (!c) throw_error_oom(); + + memcpy(c, value.begin, length * sizeof(char_t)); + c[length] = 0; + + return c; + } + else return 0; + } + + xpath_ast_node* parse_function_helper(ast_type_t type0, ast_type_t type1, size_t argc, xpath_ast_node* args[2]) + { + assert(argc <= 1); + + if (argc == 1 && args[0]->rettype() != xpath_type_node_set) throw_error("Function has to be applied to node set"); + + return new (alloc_node()) xpath_ast_node(argc == 0 ? type0 : type1, xpath_type_string, args[0]); + } + + 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 new (alloc_node()) xpath_ast_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) throw_error("Function has to be applied to node set"); + return new (alloc_node()) xpath_ast_node(ast_func_count, xpath_type_number, args[0]); + } + else if (name == PUGIXML_TEXT("contains") && argc == 2) + return new (alloc_node()) xpath_ast_node(ast_func_contains, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("concat") && argc >= 2) + return new (alloc_node()) xpath_ast_node(ast_func_concat, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("ceiling") && argc == 1) + return new (alloc_node()) xpath_ast_node(ast_func_ceiling, xpath_type_number, args[0]); + + break; + + case 'f': + if (name == PUGIXML_TEXT("false") && argc == 0) + return new (alloc_node()) xpath_ast_node(ast_func_false, xpath_type_boolean); + else if (name == PUGIXML_TEXT("floor") && argc == 1) + return new (alloc_node()) xpath_ast_node(ast_func_floor, xpath_type_number, args[0]); + + break; + + case 'i': + if (name == PUGIXML_TEXT("id") && argc == 1) + return new (alloc_node()) xpath_ast_node(ast_func_id, xpath_type_node_set, args[0]); + + break; + + case 'l': + if (name == PUGIXML_TEXT("last") && argc == 0) + return new (alloc_node()) xpath_ast_node(ast_func_last, xpath_type_number); + else if (name == PUGIXML_TEXT("lang") && argc == 1) + return new (alloc_node()) xpath_ast_node(ast_func_lang, xpath_type_boolean, args[0]); + else if (name == PUGIXML_TEXT("local-name") && argc <= 1) + return parse_function_helper(ast_func_local_name_0, ast_func_local_name_1, argc, args); + + break; + + case 'n': + if (name == PUGIXML_TEXT("name") && argc <= 1) + return parse_function_helper(ast_func_name_0, ast_func_name_1, argc, args); + else if (name == PUGIXML_TEXT("namespace-uri") && argc <= 1) + return parse_function_helper(ast_func_namespace_uri_0, ast_func_namespace_uri_1, argc, args); + else if (name == PUGIXML_TEXT("normalize-space") && argc <= 1) + return new (alloc_node()) xpath_ast_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 new (alloc_node()) xpath_ast_node(ast_func_not, xpath_type_boolean, args[0]); + else if (name == PUGIXML_TEXT("number") && argc <= 1) + return new (alloc_node()) xpath_ast_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 new (alloc_node()) xpath_ast_node(ast_func_position, xpath_type_number); + + break; + + case 'r': + if (name == PUGIXML_TEXT("round") && argc == 1) + return new (alloc_node()) xpath_ast_node(ast_func_round, xpath_type_number, args[0]); + + break; + + case 's': + if (name == PUGIXML_TEXT("string") && argc <= 1) + return new (alloc_node()) xpath_ast_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 new (alloc_node()) xpath_ast_node(argc == 0 ? ast_func_string_length_0 : ast_func_string_length_1, xpath_type_string, args[0]); + else if (name == PUGIXML_TEXT("starts-with") && argc == 2) + return new (alloc_node()) xpath_ast_node(ast_func_starts_with, xpath_type_boolean, args[0], args[1]); + else if (name == PUGIXML_TEXT("substring-before") && argc == 2) + return new (alloc_node()) xpath_ast_node(ast_func_substring_before, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("substring-after") && argc == 2) + return new (alloc_node()) xpath_ast_node(ast_func_substring_after, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("substring") && (argc == 2 || argc == 3)) + return new (alloc_node()) xpath_ast_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) throw_error("Function has to be applied to node set"); + return new (alloc_node()) xpath_ast_node(ast_func_sum, xpath_type_number, args[0]); + } + + break; + + case 't': + if (name == PUGIXML_TEXT("translate") && argc == 3) + return new (alloc_node()) xpath_ast_node(ast_func_translate, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("true") && argc == 0) + return new (alloc_node()) xpath_ast_node(ast_func_true, xpath_type_boolean); + + break; + } + + throw_error("Unrecognized function or wrong parameter count"); + + return 0; + } + + 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; + } + + 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; + } + + 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) + throw_error("Unknown variable: variable set is not provided"); + + xpath_variable* var = get_variable(_variables, name.begin, name.end); + + if (!var) + throw_error("Unknown variable: variable set does not contain the given name"); + + _lexer.next(); + + return new (alloc_node()) xpath_ast_node(ast_variable, var->type(), var); + } + + case lex_open_brace: + { + _lexer.next(); + + xpath_ast_node* n = parse_expression(); + + if (_lexer.current() != lex_close_brace) + throw_error("Unmatched braces"); + + _lexer.next(); + + return n; + } + + case lex_quoted_string: + { + const char_t* value = alloc_string(_lexer.contents()); + + xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_string_constant, xpath_type_string, value); + _lexer.next(); + + return n; + } + + case lex_number: + { + double value = 0; + + if (!convert_string_to_number(_lexer.contents().begin, _lexer.contents().end, &value)) + throw_error_oom(); + + xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_number_constant, xpath_type_number, value); + _lexer.next(); + + return n; + } + + 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) + throw_error("Unrecognized function call"); + _lexer.next(); + + if (_lexer.current() != lex_close_brace) + args[argc++] = parse_expression(); + + while (_lexer.current() != lex_close_brace) + { + if (_lexer.current() != lex_comma) + throw_error("No comma between function arguments"); + _lexer.next(); + + xpath_ast_node* n = parse_expression(); + + 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: + throw_error("Unrecognizable primary expression"); + + return 0; + } + } + + // FilterExpr ::= PrimaryExpr | FilterExpr Predicate + // Predicate ::= '[' PredicateExpr ']' + // PredicateExpr ::= Expr + xpath_ast_node* parse_filter_expression() + { + xpath_ast_node* n = parse_primary_expression(); + + while (_lexer.current() == lex_open_square_brace) + { + _lexer.next(); + + xpath_ast_node* expr = parse_expression(); + + if (n->rettype() != xpath_type_node_set) throw_error("Predicate has to be applied to node set"); + + bool posinv = expr->rettype() != xpath_type_number && expr->is_posinv(); + + n = new (alloc_node()) xpath_ast_node(posinv ? ast_filter_posinv : ast_filter, xpath_type_node_set, n, expr); + + if (_lexer.current() != lex_close_square_brace) + throw_error("Unmatched square brace"); + + _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) + throw_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(); + + return new (alloc_node()) xpath_ast_node(ast_step, set, axis_self, nodetest_type_node, 0); + } + else if (_lexer.current() == lex_double_dot) + { + _lexer.next(); + + return new (alloc_node()) xpath_ast_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) throw_error("Two axis specifiers in one step"); + + axis = parse_axis_name(nt_name, axis_specified); + + if (!axis_specified) throw_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 throw_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) throw_error("Unrecognized node type"); + + nt_name = xpath_lexer_string(); + } + else if (nt_name == PUGIXML_TEXT("processing-instruction")) + { + if (_lexer.current() != lex_quoted_string) + throw_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) + throw_error("Unmatched brace near processing-instruction()"); + _lexer.next(); + } + else + throw_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 throw_error("Unrecognized node test"); + + xpath_ast_node* n = new (alloc_node()) xpath_ast_node(ast_step, set, axis, nt_type, alloc_string(nt_name)); + + xpath_ast_node* last = 0; + + while (_lexer.current() == lex_open_square_brace) + { + _lexer.next(); + + xpath_ast_node* expr = parse_expression(); + + xpath_ast_node* pred = new (alloc_node()) xpath_ast_node(ast_predicate, xpath_type_node_set, expr); + + if (_lexer.current() != lex_close_square_brace) + throw_error("Unmatched square brace"); + _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); + + while (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) + { + lexeme_t l = _lexer.current(); + _lexer.next(); + + if (l == lex_double_slash) + n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); + + n = parse_step(n); + } + + 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 = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set); + + // 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 = new (alloc_node()) xpath_ast_node(ast_step_root, xpath_type_node_set); + n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 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 + xpath_ast_node* parse_path_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 (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 (_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) throw_error("Step has to be applied to node set"); + + n = new (alloc_node()) xpath_ast_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); + } + + // select from location path + return parse_relative_location_path(n); + } + + return n; + } + else return parse_location_path(); + } + + // UnionExpr ::= PathExpr | UnionExpr '|' PathExpr + xpath_ast_node* parse_union_expression() + { + xpath_ast_node* n = parse_path_expression(); + + while (_lexer.current() == lex_union) + { + _lexer.next(); + + xpath_ast_node* expr = parse_union_expression(); + + if (n->rettype() != xpath_type_node_set || expr->rettype() != xpath_type_node_set) + throw_error("Union operator has to be applied to node sets"); + + n = new (alloc_node()) xpath_ast_node(ast_op_union, xpath_type_node_set, n, expr); + } + + return n; + } + + // UnaryExpr ::= UnionExpr | '-' UnaryExpr + xpath_ast_node* parse_unary_expression() + { + if (_lexer.current() == lex_minus) + { + _lexer.next(); + + xpath_ast_node* expr = parse_unary_expression(); + + return new (alloc_node()) xpath_ast_node(ast_op_negate, xpath_type_number, expr); + } + else return parse_union_expression(); + } + + // MultiplicativeExpr ::= UnaryExpr + // | MultiplicativeExpr '*' UnaryExpr + // | MultiplicativeExpr 'div' UnaryExpr + // | MultiplicativeExpr 'mod' UnaryExpr + xpath_ast_node* parse_multiplicative_expression() + { + xpath_ast_node* n = parse_unary_expression(); + + while (_lexer.current() == lex_multiply || (_lexer.current() == lex_string && + (_lexer.contents() == PUGIXML_TEXT("mod") || _lexer.contents() == PUGIXML_TEXT("div")))) + { + ast_type_t op = _lexer.current() == lex_multiply ? ast_op_multiply : + _lexer.contents().begin[0] == 'd' ? ast_op_divide : ast_op_mod; + _lexer.next(); + + xpath_ast_node* expr = parse_unary_expression(); + + n = new (alloc_node()) xpath_ast_node(op, xpath_type_number, n, expr); + } + + return n; + } + + // AdditiveExpr ::= MultiplicativeExpr + // | AdditiveExpr '+' MultiplicativeExpr + // | AdditiveExpr '-' MultiplicativeExpr + xpath_ast_node* parse_additive_expression() + { + xpath_ast_node* n = parse_multiplicative_expression(); + + while (_lexer.current() == lex_plus || _lexer.current() == lex_minus) + { + lexeme_t l = _lexer.current(); + + _lexer.next(); + + xpath_ast_node* expr = parse_multiplicative_expression(); + + n = new (alloc_node()) xpath_ast_node(l == lex_plus ? ast_op_add : ast_op_subtract, xpath_type_number, n, expr); + } + + return n; + } + + // RelationalExpr ::= AdditiveExpr + // | RelationalExpr '<' AdditiveExpr + // | RelationalExpr '>' AdditiveExpr + // | RelationalExpr '<=' AdditiveExpr + // | RelationalExpr '>=' AdditiveExpr + xpath_ast_node* parse_relational_expression() + { + xpath_ast_node* n = parse_additive_expression(); + + while (_lexer.current() == lex_less || _lexer.current() == lex_less_or_equal || + _lexer.current() == lex_greater || _lexer.current() == lex_greater_or_equal) + { + lexeme_t l = _lexer.current(); + _lexer.next(); + + xpath_ast_node* expr = parse_additive_expression(); + + n = new (alloc_node()) xpath_ast_node(l == lex_less ? ast_op_less : l == lex_greater ? ast_op_greater : + l == lex_less_or_equal ? ast_op_less_or_equal : ast_op_greater_or_equal, xpath_type_boolean, n, expr); + } + + return n; + } + + // EqualityExpr ::= RelationalExpr + // | EqualityExpr '=' RelationalExpr + // | EqualityExpr '!=' RelationalExpr + xpath_ast_node* parse_equality_expression() + { + xpath_ast_node* n = parse_relational_expression(); + + while (_lexer.current() == lex_equal || _lexer.current() == lex_not_equal) + { + lexeme_t l = _lexer.current(); + + _lexer.next(); + + xpath_ast_node* expr = parse_relational_expression(); + + n = new (alloc_node()) xpath_ast_node(l == lex_equal ? ast_op_equal : ast_op_not_equal, xpath_type_boolean, n, expr); + } + + return n; + } + + // AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr + xpath_ast_node* parse_and_expression() + { + xpath_ast_node* n = parse_equality_expression(); + + while (_lexer.current() == lex_string && _lexer.contents() == PUGIXML_TEXT("and")) + { + _lexer.next(); + + xpath_ast_node* expr = parse_equality_expression(); + + n = new (alloc_node()) xpath_ast_node(ast_op_and, xpath_type_boolean, n, expr); + } + + return n; + } + + // OrExpr ::= AndExpr | OrExpr 'or' AndExpr + xpath_ast_node* parse_or_expression() + { + xpath_ast_node* n = parse_and_expression(); + + while (_lexer.current() == lex_string && _lexer.contents() == PUGIXML_TEXT("or")) + { + _lexer.next(); + + xpath_ast_node* expr = parse_and_expression(); + + n = new (alloc_node()) xpath_ast_node(ast_op_or, xpath_type_boolean, n, expr); + } + + return n; + } + + // Expr ::= OrExpr + xpath_ast_node* parse_expression() + { + return parse_or_expression(); + } + + 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* result = parse_expression(); + + if (_lexer.current() != lex_eof) + { + // there are still unparsed tokens left, error + throw_error("Incorrect query"); + } + + return result; + } + + 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); + +#ifdef PUGIXML_NO_EXCEPTIONS + int error = setjmp(parser._error_handler); + + return (error == 0) ? parser.parse() : 0; +#else + return parser.parse(); +#endif + } + }; + + struct xpath_query_impl + { + static xpath_query_impl* create() + { + void* memory = global_allocate(sizeof(xpath_query_impl)); + + return new (memory) xpath_query_impl(); + } + + static void destroy(void* ptr) + { + if (!ptr) return; + + // free all allocated pages + static_cast(ptr)->alloc.release(); + + // free allocator memory (with the first page) + global_deallocate(ptr); + } + + xpath_query_impl(): root(0), alloc(&block) + { + block.next = 0; + } + + xpath_ast_node* root; + xpath_allocator alloc; + xpath_memory_block block; + }; + + xpath_string evaluate_string_impl(xpath_query_impl* impl, const xpath_node& n, xpath_stack_data& sd) + { + if (!impl) return xpath_string(); + +#ifdef PUGIXML_NO_EXCEPTIONS + if (setjmp(sd.error_handler)) return xpath_string(); +#endif + + xpath_context c(n, 1, 1); + + return impl->root->eval_string(c, sd.stack); + } +} + +namespace pugi +{ +#ifndef PUGIXML_NO_EXCEPTIONS + xpath_exception::xpath_exception(const xpath_parse_result& result): _result(result) + { + assert(result.error); + } + + const char* xpath_exception::what() const throw() + { + return _result.error; + } + + const xpath_parse_result& xpath_exception::result() const + { + return _result; + } +#endif + + xpath_node::xpath_node() + { + } + + xpath_node::xpath_node(const xml_node& node): _node(node) + { + } + + xpath_node::xpath_node(const xml_attribute& attribute, const xml_node& parent): _node(attribute ? parent : xml_node()), _attribute(attribute) + { + } + + xml_node xpath_node::node() const + { + return _attribute ? xml_node() : _node; + } + + xml_attribute xpath_node::attribute() const + { + return _attribute; + } + + xml_node xpath_node::parent() const + { + return _attribute ? _node : _node.parent(); + } + + xpath_node::operator xpath_node::unspecified_bool_type() const + { + return (_node || _attribute) ? &xpath_node::_node : 0; + } + + bool xpath_node::operator!() const + { + return !(_node || _attribute); + } + + bool xpath_node::operator==(const xpath_node& n) const + { + return _node == n._node && _attribute == n._attribute; + } + + bool xpath_node::operator!=(const xpath_node& n) const + { + return _node != n._node || _attribute != n._attribute; + } + +#ifdef __BORLANDC__ + bool operator&&(const xpath_node& lhs, bool rhs) + { + return (bool)lhs && rhs; + } + + bool operator||(const xpath_node& lhs, bool rhs) + { + return (bool)lhs || rhs; + } +#endif + + void xpath_node_set::_assign(const_iterator begin, const_iterator end) + { + assert(begin <= end); + + size_t size = static_cast(end - begin); + + if (size <= 1) + { + // deallocate old buffer + if (_begin != &_storage) global_deallocate(_begin); + + // use internal buffer + if (begin != end) _storage = *begin; + + _begin = &_storage; + _end = &_storage + size; + } + else + { + // make heap copy + xpath_node* storage = static_cast(global_allocate(size * sizeof(xpath_node))); + + if (!storage) + { +#ifdef PUGIXML_NO_EXCEPTIONS + return; +#else + throw std::bad_alloc(); +#endif + } + + memcpy(storage, begin, size * sizeof(xpath_node)); + + // deallocate old buffer + if (_begin != &_storage) global_deallocate(_begin); + + // finalize + _begin = storage; + _end = storage + size; + } + } + + xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(&_storage), _end(&_storage) + { + } + + xpath_node_set::xpath_node_set(const_iterator begin, const_iterator end, type_t type): _type(type), _begin(&_storage), _end(&_storage) + { + _assign(begin, end); + } + + xpath_node_set::~xpath_node_set() + { + if (_begin != &_storage) global_deallocate(_begin); + } + + xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(ns._type), _begin(&_storage), _end(&_storage) + { + _assign(ns._begin, ns._end); + } + + xpath_node_set& xpath_node_set::operator=(const xpath_node_set& ns) + { + if (this == &ns) return *this; + + _type = ns._type; + _assign(ns._begin, ns._end); + + return *this; + } + + xpath_node_set::type_t xpath_node_set::type() const + { + return _type; + } + + size_t xpath_node_set::size() const + { + return _end - _begin; + } + + bool xpath_node_set::empty() const + { + return _begin == _end; + } + + const xpath_node& xpath_node_set::operator[](size_t index) const + { + assert(index < size()); + return _begin[index]; + } + + xpath_node_set::const_iterator xpath_node_set::begin() const + { + return _begin; + } + + xpath_node_set::const_iterator xpath_node_set::end() const + { + return _end; + } + + void xpath_node_set::sort(bool reverse) + { + _type = xpath_sort(_begin, _end, _type, reverse); + } + + xpath_node xpath_node_set::first() const + { + return xpath_first(_begin, _end, _type); + } + + xpath_parse_result::xpath_parse_result(): error("Internal error"), offset(0) + { + } + + xpath_parse_result::operator bool() const + { + return error == 0; + } + const char* xpath_parse_result::description() const + { + return error ? error : "No error"; + } + + xpath_variable::xpath_variable() + { + } + + 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(!"Invalid variable type"); + return 0; + } + } + + xpath_value_type xpath_variable::type() const + { + return _type; + } + + bool xpath_variable::get_boolean() const + { + return (_type == xpath_type_boolean) ? static_cast(this)->value : false; + } + + double xpath_variable::get_number() const + { + return (_type == xpath_type_number) ? static_cast(this)->value : gen_nan(); + } + + 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(""); + } + + const xpath_node_set& xpath_variable::get_node_set() const + { + return (_type == xpath_type_node_set) ? static_cast(this)->value : dummy_node_set; + } + + bool xpath_variable::set(bool value) + { + if (_type != xpath_type_boolean) return false; + + static_cast(this)->value = value; + return true; + } + + bool xpath_variable::set(double value) + { + if (_type != xpath_type_number) return false; + + static_cast(this)->value = value; + return true; + } + + bool xpath_variable::set(const char_t* value) + { + if (_type != xpath_type_string) return false; + + xpath_variable_string* var = static_cast(this); + + // duplicate string + size_t size = (strlength(value) + 1) * sizeof(char_t); + + char_t* copy = static_cast(global_allocate(size)); + if (!copy) return false; + + memcpy(copy, value, size); + + // replace old string + if (var->value) global_deallocate(var->value); + var->value = copy; + + return true; + } + + bool xpath_variable::set(const xpath_node_set& value) + { + if (_type != xpath_type_node_set) return false; + + static_cast(this)->value = value; + return true; + } + + xpath_variable_set::xpath_variable_set() + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) _data[i] = 0; + } + + xpath_variable_set::~xpath_variable_set() + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + { + xpath_variable* var = _data[i]; + + while (var) + { + xpath_variable* next = var->_next; + + delete_xpath_variable(var->_type, var); + + var = next; + } + } + } + + xpath_variable* xpath_variable_set::find(const char_t* name) const + { + const size_t hash_size = sizeof(_data) / sizeof(_data[0]); + size_t hash = hash_string(name) % hash_size; + + // look for existing variable + for (xpath_variable* var = _data[hash]; var; var = var->_next) + if (strequal(var->name(), name)) + return var; + + return 0; + } + + 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 = hash_string(name) % hash_size; + + // look for existing variable + for (xpath_variable* var = _data[hash]; var; var = var->_next) + if (strequal(var->name(), name)) + return var->type() == type ? var : 0; + + // add new variable + xpath_variable* result = new_xpath_variable(type, name); + + if (result) + { + result->_type = type; + result->_next = _data[hash]; + + _data[hash] = result; + } + + return result; + } + + 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; + } + + 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; + } + + 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; + } + + 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; + } + + xpath_variable* xpath_variable_set::get(const char_t* name) + { + return find(name); + } + + const xpath_variable* xpath_variable_set::get(const char_t* name) const + { + return find(name); + } + + xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables): _impl(0) + { + xpath_query_impl* impl = xpath_query_impl::create(); + + if (!impl) + { +#ifdef PUGIXML_NO_EXCEPTIONS + _result.error = "Out of memory"; +#else + throw std::bad_alloc(); +#endif + } + else + { + buffer_holder impl_holder(impl, xpath_query_impl::destroy); + + impl->root = xpath_parser::parse(query, variables, &impl->alloc, &_result); + + if (impl->root) + { + _impl = static_cast(impl_holder.release()); + _result.error = 0; + } + } + } + + xpath_query::~xpath_query() + { + xpath_query_impl::destroy(_impl); + } + + xpath_value_type xpath_query::return_type() const + { + if (!_impl) return xpath_type_none; + + return static_cast(_impl)->root->rettype(); + } + + bool xpath_query::evaluate_boolean(const xpath_node& n) const + { + if (!_impl) return false; + + xpath_context c(n, 1, 1); + xpath_stack_data sd; + +#ifdef PUGIXML_NO_EXCEPTIONS + if (setjmp(sd.error_handler)) return false; +#endif + + return static_cast(_impl)->root->eval_boolean(c, sd.stack); + } + + double xpath_query::evaluate_number(const xpath_node& n) const + { + if (!_impl) return gen_nan(); + + xpath_context c(n, 1, 1); + xpath_stack_data sd; + +#ifdef PUGIXML_NO_EXCEPTIONS + if (setjmp(sd.error_handler)) return gen_nan(); +#endif + + return static_cast(_impl)->root->eval_number(c, sd.stack); + } + +#ifndef PUGIXML_NO_STL + string_t xpath_query::evaluate_string(const xpath_node& n) const + { + xpath_stack_data sd; + + return evaluate_string_impl(static_cast(_impl), n, sd).c_str(); + } +#endif + + size_t xpath_query::evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const + { + xpath_stack_data sd; + + xpath_string r = evaluate_string_impl(static_cast(_impl), n, sd); + + 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; + } + + xpath_node_set xpath_query::evaluate_node_set(const xpath_node& n) const + { + if (!_impl) return xpath_node_set(); + + xpath_ast_node* root = static_cast(_impl)->root; + + if (root->rettype() != xpath_type_node_set) + { +#ifdef PUGIXML_NO_EXCEPTIONS + return xpath_node_set(); +#else + xpath_parse_result result; + result.error = "Expression does not evaluate to node set"; + + throw xpath_exception(result); +#endif + } + + xpath_context c(n, 1, 1); + xpath_stack_data sd; + +#ifdef PUGIXML_NO_EXCEPTIONS + if (setjmp(sd.error_handler)) return xpath_node_set(); +#endif + + xpath_node_set_raw r = root->eval_node_set(c, sd.stack); + + return xpath_node_set(r.begin(), r.end(), r.type()); + } + + const xpath_parse_result& xpath_query::result() const + { + return _result; + } + + xpath_query::operator xpath_query::unspecified_bool_type() const + { + return _impl ? &xpath_query::_impl : 0; + } + + bool xpath_query::operator!() const + { + return !_impl; + } + + xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables) const + { + xpath_query q(query, variables); + return select_single_node(q); + } + + xpath_node xml_node::select_single_node(const xpath_query& query) const + { + xpath_node_set s = query.evaluate_node_set(*this); + return s.empty() ? xpath_node() : s.first(); + } + + xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables) const + { + xpath_query q(query, variables); + return select_nodes(q); + } + + xpath_node_set xml_node::select_nodes(const xpath_query& query) const + { + return query.evaluate_node_set(*this); + } +} + +#endif + +/** +* Copyright (c) 2006-2010 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. +*/ \ No newline at end of file diff --git a/LifeGraphics/pugixml/src/pugixml.hpp b/LifeGraphics/pugixml/src/pugixml.hpp new file mode 100644 index 0000000..caa2180 --- /dev/null +++ b/LifeGraphics/pugixml/src/pugixml.hpp @@ -0,0 +1,1131 @@ +/** +* pugixml parser - version 1.0 +* -------------------------------------------------------- +* Copyright (C) 2006-2010, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) +* Report bugs and download new versions at http://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_PUGIXML_HPP +#define HEADER_PUGIXML_HPP + +#include "pugiconfig.hpp" + +#ifndef PUGIXML_NO_STL +namespace std +{ + struct bidirectional_iterator_tag; + +#ifdef __SUNPRO_CC + // Sun C++ compiler has a bug which forces template argument names in forward declarations to be the same as in actual definitions + template class allocator; + template struct char_traits; + template class basic_istream; + template class basic_ostream; + template class basic_string; +#else + // Borland C++ compiler has a bug which forces template argument names in forward declarations to be the same as in actual definitions + template class allocator; + template struct char_traits; + template class basic_istream; + template class basic_ostream; + template class basic_string; +#endif + + // Digital Mars compiler has a bug which requires a forward declaration for explicit instantiation (otherwise type selection is messed up later, producing link errors) + // Also note that we have to declare char_traits as a class here, since it's defined that way +#ifdef __DMC__ + template <> class char_traits; +#endif +} +#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 + +// Include exception header for XPath +#if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS) +# include +#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 + +#include + +// 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; + + // 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 < / >& stream); + xml_writer_stream(std::basic_ostream >& stream); + + virtual void write(const void* data, size_t size); + + 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 xml_attribute_struct* xml_attribute::*unspecified_bool_type; + + 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 as a number, or 0 if conversion did not succeed or attribute is empty + int as_int() const; + unsigned int as_uint() const; + double as_double() const; + float as_float() const; + + // Get attribute value as bool (returns true if first character is in '1tTyY' set), or false if attribute is empty + bool as_bool() 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(double rhs); + bool set_value(bool rhs); + + // 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=(double rhs); + xml_attribute& operator=(bool rhs); + + // 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; + + protected: + xml_node_struct* _root; + + typedef xml_node_struct* xml_node::*unspecified_bool_type; + + 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/value, or "" if node is empty or it has no name/value + const char_t* name() const; + 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 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 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); + + // 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); + + // 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_single_node(const char_t* query, xpath_variable_set* variables = 0) const; + xpath_node select_single_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; +#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; + + // 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 + + // Child node iterator (a bidirectional iterator over a collection of xml_node) + class PUGIXML_CLASS xml_node_iterator + { + friend class xml_node; + + private: + 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*(); + xml_node* operator->(); + + 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: + 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*(); + xml_attribute* operator->(); + + const xml_attribute_iterator& operator++(); + xml_attribute_iterator operator++(int); + + const xml_attribute_iterator& operator--(); + xml_attribute_iterator operator--(int); + }; + + // 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) + }; + + // 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&); + const xml_document& operator=(const xml_document&); + + void create(); + void destroy(); + + xml_parse_result load_buffer_impl(void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own); + + public: + // Default constructor, makes empty document + xml_document(); + + // Destructor, invalidates all node/attribute handles to this document + ~xml_document(); + + // 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 + + // Load document from zero-terminated string. No encoding conversions are applied. + xml_parse_result load(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(); + + // 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]; + + // Non-copyable semantics + xpath_variable_set(const xpath_variable_set&); + xpath_variable_set& operator=(const xpath_variable_set&); + + xpath_variable* find(const char_t* name) const; + + public: + // Default constructor/destructor + xpath_variable_set(); + ~xpath_variable_set(); + + // 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* xpath_query::*unspecified_bool_type; + + // 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); + + // Destructor + ~xpath_query(); + + // 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; + + // 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 + // 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(); + + // Get parse result + const xpath_parse_result& result() const; + }; +#endif + + // XPath node class (either xml_node or xml_attribute) + class PUGIXML_CLASS xpath_node + { + private: + xml_node _node; + xml_attribute _attribute; + + typedef xml_node xpath_node::*unspecified_bool_type; + + 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; + + // 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); + + // 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; + + xpath_node* _begin; + xpath_node* _end; + + void _assign(const_iterator begin, const_iterator end); + }; +#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&); +} +#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&); +} +#endif + +#endif + +/** +* Copyright (c) 2006-2010 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. +*/ \ No newline at end of file diff --git a/LifeGraphics/queries/IFrustumQuery.cpp b/LifeGraphics/queries/IFrustumQuery.cpp new file mode 100644 index 0000000..69d2db2 --- /dev/null +++ b/LifeGraphics/queries/IFrustumQuery.cpp @@ -0,0 +1,13 @@ +#include "queries/IFrustumQuery.h" + +namespace LifeGraphics +{ + void IFrustumQuery::SetFrustum(const Frustum& _frustum) + { + frustum = _frustum; + } + const Frustum& IFrustumQuery::GetFrustum() + { + return frustum; + } +} \ No newline at end of file diff --git a/LifeGraphics/queries/IFrustumQuery.h b/LifeGraphics/queries/IFrustumQuery.h new file mode 100644 index 0000000..677116e --- /dev/null +++ b/LifeGraphics/queries/IFrustumQuery.h @@ -0,0 +1,29 @@ +#ifndef IFrustumQuery_h__ +#define IFrustumQuery_h__ + +#include "queries/INodeQuery.h" +#include "camera/Frustum.h" + +namespace LifeGraphics +{ + class IFrustumQuery : public INodeQuery + { + public: + IFrustumQuery() + { + Filration = false; + filter = 0; + } + IGraphicsNode* operator [](int index) + { + return nodes[index]; + } + void SetFrustum(const Frustum& _frustum); + const Frustum& GetFrustum(); + int filter; + bool Filration; + private: + Frustum frustum; + }; +} +#endif // IFrustumQuery_h__ \ No newline at end of file diff --git a/LifeGraphics/queries/INodeQuery.h b/LifeGraphics/queries/INodeQuery.h new file mode 100644 index 0000000..279fdf6 --- /dev/null +++ b/LifeGraphics/queries/INodeQuery.h @@ -0,0 +1,31 @@ +#ifndef INodeQuery_h__ +#define INodeQuery_h__ + +#include "queries/IQuery.h" +#include "IGraphicsNode.h" +#include + +namespace LifeGraphics +{ + class INodeQuery : public IQuery + { + public: + void Flush() + { + nodes.clear(); + } + void AddNode(IGraphicsNode* node) + { + nodes.push_back(node); + } + int Size() + { + return nodes.size(); + } + protected: + typedef std::vector NodeList; + NodeList nodes; + }; +} + +#endif // INodeQuery_h__ \ No newline at end of file diff --git a/LifeGraphics/queries/IQuery.h b/LifeGraphics/queries/IQuery.h new file mode 100644 index 0000000..3960a7f --- /dev/null +++ b/LifeGraphics/queries/IQuery.h @@ -0,0 +1,13 @@ +#ifndef IQuery_h__ +#define IQuery_h__ + +namespace LifeGraphics +{ + class IQuery + { + public: + virtual ~IQuery() {} + virtual void Flush() = 0; + }; +} +#endif // IQuery_h__ \ No newline at end of file diff --git a/LifeGraphics/render/Enums.h b/LifeGraphics/render/Enums.h new file mode 100644 index 0000000..359bb3a --- /dev/null +++ b/LifeGraphics/render/Enums.h @@ -0,0 +1,182 @@ +#ifndef Enums_h__ +#define Enums_h__ + +enum COMPARISON_FUNC +{ COMPARISON_NEVER = 1, +COMPARISON_LESS = 2, +COMPARISON_EQUAL = 3, +COMPARISON_LESS_EQUAL = 4, +COMPARISON_GREATER = 5, +COMPARISON_NOT_EQUAL = 6, +COMPARISON_GREATER_EQUAL = 7, +COMPARISON_ALWAYS = 8 +}; + +enum STENCIL_OP +{ + STENCIL_OP_KEEP = 1, + STENCIL_OP_ZERO = 2, + STENCIL_OP_REPLACE = 3, + STENCIL_OP_INCR_SAT = 4, + STENCIL_OP_DECR_SAT = 5, + STENCIL_OP_INVERT = 6, + STENCIL_OP_INCR = 7, + STENCIL_OP_DECR = 8 +}; + +enum CULL_MODE { + CULL_NONE = 1, + CULL_FRONT = 2, + CULL_BACK = 3 +}; + +enum CLEAR_FLAGS +{ + CF_COLOR_TARGET = 1, + CF_DEPTH_BUFFER = 2, + CF_STENCIL = 4 +}; + +enum PRIMITIVE_TOPOLOGY +{ + PT_POINT_LIST = 1, + PT_LINE_LIST = 2, + PT_LINE_STRIP = 3, + PT_TRIANGLE_LIST = 4, + PT_TRIANGLE_STRIP = 5, +}; + +enum BLEND_OP +{ + BLEND_OP_ADD = 1, + BLEND_OP_SUBTRACT = 2, + BLEND_OP_REV_SUBTRACT = 3, + BLEND_OP_MIN = 4, + BLEND_OP_MAX = 5 +}; + +enum BLEND_SRC +{ BLEND_ZERO = 1, +BLEND_ONE = 2, +BLEND_SRC_COLOR = 3, +BLEND_INV_SRC_COLOR = 4, +BLEND_SRC_ALPHA = 5, +BLEND_INV_SRC_ALPHA = 6, +BLEND_DEST_ALPHA = 7, +BLEND_INV_DEST_ALPHA = 8, +BLEND_DEST_COLOR = 9, +BLEND_INV_DEST_COLOR = 10, +BLEND_SRC_ALPHA_SAT = 11, +BLEND_BLEND_FACTOR = 14, +BLEND_INV_BLEND_FACTOR = 15, +BLEND_SRC1_COLOR = 16, +BLEND_INV_SRC1_COLOR = 17, +BLEND_SRC1_ALPHA = 18, +BLEND_INV_SRC1_ALPHA = 19 +}; + +enum IDataFormat +{ + FORMAT_UNKNOWN = 0, + FORMAT_R32G32B32A32_TYPELESS = 1, + FORMAT_R32G32B32A32_FLOAT = 2, + FORMAT_R32G32B32A32_UINT = 3, + FORMAT_R32G32B32A32_SINT = 4, + FORMAT_R32G32B32_TYPELESS = 5, + FORMAT_R32G32B32_FLOAT = 6, + FORMAT_R32G32B32_UINT = 7, + FORMAT_R32G32B32_SINT = 8, + FORMAT_R16G16B16A16_TYPELESS = 9, + FORMAT_R16G16B16A16_FLOAT = 10, + FORMAT_R16G16B16A16_UNORM = 11, + FORMAT_R16G16B16A16_UINT = 12, + FORMAT_R16G16B16A16_SNORM = 13, + FORMAT_R16G16B16A16_SINT = 14, + FORMAT_R32G32_TYPELESS = 15, + FORMAT_R32G32_FLOAT = 16, + FORMAT_R32G32_UINT = 17, + FORMAT_R32G32_SINT = 18, + FORMAT_R32G8X24_TYPELESS = 19, + FORMAT_D32_FLOAT_S8X24_UINT = 20, + FORMAT_R32_FLOAT_X8X24_TYPELESS = 21, + FORMAT_X32_TYPELESS_G8X24_UINT = 22, + FORMAT_R10G10B10A2_TYPELESS = 23, + FORMAT_R10G10B10A2_UNORM = 24, + FORMAT_R10G10B10A2_UINT = 25, + FORMAT_R11G11B10_FLOAT = 26, + FORMAT_R8G8B8A8_TYPELESS = 27, + FORMAT_R8G8B8A8_UNORM = 28, + FORMAT_R8G8B8A8_UNORM_SRGB = 29, + FORMAT_R8G8B8A8_UINT = 30, + FORMAT_R8G8B8A8_SNORM = 31, + FORMAT_R8G8B8A8_SINT = 32, + FORMAT_R16G16_TYPELESS = 33, + FORMAT_R16G16_FLOAT = 34, + FORMAT_R16G16_UNORM = 35, + FORMAT_R16G16_UINT = 36, + FORMAT_R16G16_SNORM = 37, + FORMAT_R16G16_SINT = 38, + FORMAT_R32_TYPELESS = 39, + FORMAT_D32_FLOAT = 40, + FORMAT_R32_FLOAT = 41, + FORMAT_R32_UINT = 42, + FORMAT_R32_SINT = 43, + FORMAT_R24G8_TYPELESS = 44, + FORMAT_D24_UNORM_S8_UINT = 45, + FORMAT_R24_UNORM_X8_TYPELESS = 46, + FORMAT_X24_TYPELESS_G8_UINT = 47, + FORMAT_R8G8_TYPELESS = 48, + FORMAT_R8G8_UNORM = 49, + FORMAT_R8G8_UINT = 50, + FORMAT_R8G8_SNORM = 51, + FORMAT_R8G8_SINT = 52, + FORMAT_R16_TYPELESS = 53, + FORMAT_R16_FLOAT = 54, + FORMAT_D16_UNORM = 55, + FORMAT_R16_UNORM = 56, + FORMAT_R16_UINT = 57, + FORMAT_R16_SNORM = 58, + FORMAT_R16_SINT = 59, + FORMAT_R8_TYPELESS = 60, + FORMAT_R8_UNORM = 61, + FORMAT_R8_UINT = 62, + FORMAT_R8_SNORM = 63, + FORMAT_R8_SINT = 64, + FORMAT_A8_UNORM = 65, + FORMAT_R1_UNORM = 66, + FORMAT_R9G9B9E5_SHAREDEXP = 67, + FORMAT_R8G8_B8G8_UNORM = 68, + FORMAT_G8R8_G8B8_UNORM = 69, + FORMAT_BC1_TYPELESS = 70, + FORMAT_BC1_UNORM = 71, + FORMAT_BC1_UNORM_SRGB = 72, + FORMAT_BC2_TYPELESS = 73, + FORMAT_BC2_UNORM = 74, + FORMAT_BC2_UNORM_SRGB = 75, + FORMAT_BC3_TYPELESS = 76, + FORMAT_BC3_UNORM = 77, + FORMAT_BC3_UNORM_SRGB = 78, + FORMAT_BC4_TYPELESS = 79, + FORMAT_BC4_UNORM = 80, + FORMAT_BC4_SNORM = 81, + FORMAT_BC5_TYPELESS = 82, + FORMAT_BC5_UNORM = 83, + FORMAT_BC5_SNORM = 84, + FORMAT_B5G6R5_UNORM = 85, + FORMAT_B5G5R5A1_UNORM = 86, + FORMAT_B8G8R8A8_UNORM = 87, + FORMAT_B8G8R8X8_UNORM = 88, + FORMAT_R10G10B10_XR_BIAS_A2_UNORM = 89, + FORMAT_B8G8R8A8_TYPELESS = 90, + FORMAT_B8G8R8A8_UNORM_SRGB = 91, + FORMAT_B8G8R8X8_TYPELESS = 92, + FORMAT_B8G8R8X8_UNORM_SRGB = 93, + FORMAT_BC6H_TYPELESS = 94, + FORMAT_BC6H_UF16 = 95, + FORMAT_BC6H_SF16 = 96, + FORMAT_BC7_TYPELESS = 97, + FORMAT_BC7_UNORM = 98, + FORMAT_BC7_UNORM_SRGB = 99, + FORMAT_FORCE_UINT = 0xffffffff +}; +#endif // Enums_h__ \ No newline at end of file diff --git a/LifeGraphics/render/IBlendState.h b/LifeGraphics/render/IBlendState.h new file mode 100644 index 0000000..c5b0ddd --- /dev/null +++ b/LifeGraphics/render/IBlendState.h @@ -0,0 +1,19 @@ +#ifndef IBlendState_h__ +#define IBlendState_h__ + +namespace LifeGraphics +{ + class IBlendState + { + public: + IBlendState() + { + } + virtual ~IBlendState() + { + } + bool AlphaToCoverageEnabled; + bool AlphaBlendEnabled; + }; +} +#endif // IBlendState_h__ \ No newline at end of file diff --git a/LifeGraphics/render/IConstantBuffer.h b/LifeGraphics/render/IConstantBuffer.h new file mode 100644 index 0000000..dd4a180 --- /dev/null +++ b/LifeGraphics/render/IConstantBuffer.h @@ -0,0 +1,16 @@ +#ifndef IConstantBuffer_h__ +#define IConstantBuffer_h__ +#include "IResource.h" + +namespace LifeGraphics +{ + class IConstantBuffer : public IResource + { + public: + IConstantBuffer(){} + virtual ~IConstantBuffer(){} + virtual void Update(void* pData, int dataLength) = 0; + }; +} + +#endif // IConstantBuffer_h__ \ No newline at end of file diff --git a/LifeGraphics/render/IConstantMapping.cpp b/LifeGraphics/render/IConstantMapping.cpp new file mode 100644 index 0000000..98b46e3 --- /dev/null +++ b/LifeGraphics/render/IConstantMapping.cpp @@ -0,0 +1,45 @@ +#include "render/IConstantMapping.h" + +IConstantMapping::IConstantMapping() + :lastSlotId(0) +{ +} +IConstantMapping::~IConstantMapping() +{ +} +uint32 IConstantMapping::GetAlignedSize() +{ + return (lastSlotId + 1) * HLSL_CONSTANT_SIZE * (mapping.size() > 0 ? 1 : 0); +} +void IConstantMapping::InsertConstant(const String& name, uint32 numRows, uint32 numCols) +{ + if(!numRows || !numCols) return; + + if(numRows == 1) + { + //try to insert variable in @white spaces@ + if(spaces.size() > 0) + { + for(ConstantCollectionItr it = spaces.begin(); it != spaces.end(); ++it) + { + if((*it).ColumnCount <= numCols) //found suitable white space! Fill it ^^ + { + mapping.push_back(IConstantMappingData((*it).SlotID, (*it).SlotOffset, 1, numCols)); + (*it).SlotOffset += numCols; + (*it).ColumnCount -= numCols; + if((*it).ColumnCount <= 0) + spaces.erase(it); + return; + } + } + } + mapping.push_back(IConstantMappingData(lastSlotId++, 0, 1, numCols)); + if(numCols < 4) + spaces.push_back(IConstantMappingData(lastSlotId, numCols, 1, 4 - numCols)); + } + else + { + mapping.push_back(IConstantMappingData(lastSlotId, 0, numRows, numCols)); + lastSlotId+=numRows; + } +} \ No newline at end of file diff --git a/LifeGraphics/render/IConstantMapping.h b/LifeGraphics/render/IConstantMapping.h new file mode 100644 index 0000000..3fd68a1 --- /dev/null +++ b/LifeGraphics/render/IConstantMapping.h @@ -0,0 +1,38 @@ +#ifndef IConstantMapping_h__ +#define IConstantMapping_h__ + +#include +#include "Types.h" +#include + +#define HLSL_CONSTANT_SIZE (4*4) + +struct IConstantMappingData +{ + IConstantMappingData(uint32 slot, uint32 offset, uint32 row_count, uint32 col_count) + { + SlotID = slot; + SlotOffset = offset; + RowCount = row_count; + ColumnCount = col_count; + } + uint32 SlotID; + uint32 SlotOffset; + uint32 ColumnCount; + uint32 RowCount; +}; +class IConstantMapping +{ +public: + IConstantMapping(); + ~IConstantMapping(); + void InsertConstant(const String& name, uint32 numRows, uint32 numCols); + uint32 GetAlignedSize(); +private: + uint32 lastSlotId; + typedef std::vector ConstantCollection; + typedef std::vector::iterator ConstantCollectionItr; + ConstantCollection mapping; + ConstantCollection spaces; +}; +#endif // IConstantMapping_h__ \ No newline at end of file diff --git a/LifeGraphics/render/IDataBuffer.h b/LifeGraphics/render/IDataBuffer.h new file mode 100644 index 0000000..dccf8d3 --- /dev/null +++ b/LifeGraphics/render/IDataBuffer.h @@ -0,0 +1,30 @@ +#ifndef I_DATA_BUFFER_H__ +#define I_DATA_BUFFER_H__ + +#include "IDataStream.h" +#include "IResource.h" +namespace LifeGraphics +{ + enum IBufferLockFlags + { + LF_None = 0, + LF_DoNotCopyData = 1, + LF_ReadOnly = 16, + LF_NoSystemLock = 2048, + LF_NoOverwrite = 4096, + LF_Discard = 8192, + LF_DoNotWait = 16384, + LF_NoDirtyUpdate = 32768, + }; + + class IDataBuffer : public IResource + { + public: + virtual ~IDataBuffer(){} + virtual LifeCore::IDataStream* Map(int offset, int flags) = 0; + virtual void Unmap() = 0; + int length; + }; +} + +#endif \ No newline at end of file diff --git a/LifeGraphics/render/IDepthStencilState.h b/LifeGraphics/render/IDepthStencilState.h new file mode 100644 index 0000000..dfc01b0 --- /dev/null +++ b/LifeGraphics/render/IDepthStencilState.h @@ -0,0 +1,17 @@ +#ifndef IDepthStencilState_h__ +#define IDepthStencilState_h__ + +namespace LifeGraphics +{ + class IDepthStencilState + { + public: + IDepthStencilState() + { + } + virtual ~IDepthStencilState() + { + } + }; +} +#endif // IDepthStencilState_h__ \ No newline at end of file diff --git a/LifeGraphics/render/IIndexBuffer.h b/LifeGraphics/render/IIndexBuffer.h new file mode 100644 index 0000000..b3b6de3 --- /dev/null +++ b/LifeGraphics/render/IIndexBuffer.h @@ -0,0 +1,17 @@ +#ifndef I_INDEX_BUFFER_H__ +#define I_INDEX_BUFFER_H__ + +#include "IDataBuffer.h" + +namespace LifeGraphics +{ + class IIndexBuffer : public IDataBuffer + { + public: + virtual ~IIndexBuffer(){} + int isShort; + int indexCount; + }; +} + +#endif \ No newline at end of file diff --git a/LifeGraphics/render/IInputElement.h b/LifeGraphics/render/IInputElement.h new file mode 100644 index 0000000..a0f148d --- /dev/null +++ b/LifeGraphics/render/IInputElement.h @@ -0,0 +1,44 @@ +#ifndef IInputElement_h__ +#define IInputElement_h__ + +#include "LString.h" + +namespace LifeGraphics +{ + struct IInputElement + { + String Name; + int SemanticIndex; + int Offset; + int Format; + int Slot; + int IsInstancingData; + int InstancingRate; + + IInputElement(String name, int semanticIndex, int offset, int format, int slot, int isInstData, int instancingRate) + { + Name = name; + SemanticIndex = semanticIndex; + Offset = offset; + Format = format; + Slot = slot; + IsInstancingData = isInstData; + InstancingRate = instancingRate; + } + + IInputElement(String name, int semanticIndex, int offset, int format, int slot) + { + Name = name; + SemanticIndex = semanticIndex; + Offset = offset; + Format = format; + Slot = slot; + IsInstancingData = 0; + InstancingRate = 0; + } + IInputElement() + { + } + }; +} +#endif // IInputElement_h__ \ No newline at end of file diff --git a/LifeGraphics/render/IInputLayout.h b/LifeGraphics/render/IInputLayout.h new file mode 100644 index 0000000..efbc4a9 --- /dev/null +++ b/LifeGraphics/render/IInputLayout.h @@ -0,0 +1,30 @@ +#ifndef IInputLayout_h__ +#define IInputLayout_h__ + +#include "render/IInputElement.h" + +namespace LifeGraphics +{ + class IInputLayout : public IResource + { + public: + IInputLayout():elementCount(0) + { + } + virtual ~IInputLayout() + { + } + int count() + { + return elementCount; + } + IInputElement* GetElements() const + { + return elements; + } + protected: + IInputElement* elements; + int elementCount; + }; +} +#endif // IInputLayout_h__ \ No newline at end of file diff --git a/LifeGraphics/render/IMacroDefine.h b/LifeGraphics/render/IMacroDefine.h new file mode 100644 index 0000000..566f147 --- /dev/null +++ b/LifeGraphics/render/IMacroDefine.h @@ -0,0 +1,25 @@ +#ifndef IMacroDefine_h__ +#define IMacroDefine_h__ +#include "LString.h" +namespace LifeGraphics +{ + class IMacroDefine + { + public: + IMacroDefine(){} + ~IMacroDefine(){} + IMacroDefine(std::string& _name) + : definition("true") + { + name = _name; + } + IMacroDefine(std::string& _name, std::string& _def) + { + name = _name; + definition = _def; + } + std::string name; + std::string definition; + }; +} +#endif // IMacroDefine_h__ \ No newline at end of file diff --git a/LifeGraphics/render/IMaterial.cpp b/LifeGraphics/render/IMaterial.cpp new file mode 100644 index 0000000..468e38e --- /dev/null +++ b/LifeGraphics/render/IMaterial.cpp @@ -0,0 +1,232 @@ +#include "render/IMaterial.h" +#include "render/IMaterialProxy.h" + +namespace LifeGraphics +{ + +IMaterialPass::IMaterialPass(MaterialPass _type) + :passType(_type), ps(nullptr), vs(nullptr), gs(nullptr), cb(nullptr) +{ +} +IMaterialPass::~IMaterialPass() +{ +} +void IMaterialPass::SetShader(SHADER_TYPE _type, IShader* shader) +{ + switch(_type) + { + case ST_VS: + vs = shader; + break; + case ST_PS: + ps = shader; + break; + case ST_GS: + gs = shader; + break; + default: + { + //TODO replace it by new log error + printf("Unknown shader type in IMaterialPass::SetShader\n"); + } + } +} +IShader* IMaterialPass::GetShader(SHADER_TYPE _type) +{ + switch(_type) + { + case ST_VS: + return vs; + case ST_PS: + return ps; + case ST_GS: + return gs; + default: + { + //TODO replace it by new log error + printf("Unknown shader type in IMaterialPass::GetShader\n"); + } + } +} +MaterialPass IMaterialPass::GetPassType() +{ + return passType; +} +IConstantBuffer* IMaterialPass::GetConstantBuffer() +{ + return cb; +} +void IMaterialPass::SetTexture(texture_ptr tex, uint32 slot) +{ + textureSlots[slot] = tex; +} +void IMaterialPass::Apply() +{ + for(int i = 0; i < MAX_TEX_SLOT; i++) + { + texture_ptr tex = textureSlots[i]; + if(tex.ptr() != nullptr) + { + CG::me()->GetRenderer()->SetPSTexture(tex.ptr(), i); + } + } + + CG::me()->GetRenderer()->SetPixelShader(ps); + CG::me()->GetRenderer()->SetVertexShader(vs); +} + +IMaterial::IMaterial(IShaderGraph* _graph, IShaderGraphCompiler* _compiler) + :CastShadows(false), ReceiveShadows(false), Opaque(false), flags(MF_NONE) +{ + graph = _graph; + compiler = _compiler; +} +IMaterial::~IMaterial() +{ +} +IMaterialPass* IMaterial::AddPass(MaterialPass _type) +{ + IMaterialPass* result = GetPass(_type); + if(result == nullptr) + { + result = new IMaterialPass(_type); + passes[_type] = result; + } + return result; +} +void IMaterial::SetTexture(int id, texture_ptr tex) +{ + textures[id] = tex; +} +texture_ptr IMaterial::GetTexture(int id) +{ + return textures[id]; +} +IMaterialPass* IMaterial::GetPass(MaterialPass _type) +{ + PassCollectionItr it = passes.find(_type); + return it == passes.end() ? nullptr : it->second; +} +uint32 IMaterial::GetPassCount() +{ + return passes.size(); +} +IMaterialProxy* IMaterial::CreateProxy(IObjectMesh* parent) +{ + IMaterialProxy* proxy = new IMaterialProxy(this, parent); + //TODO + //Add proxy to watch collection! + return proxy; +} +IMaterialTechnique* IMaterial::CreateTechnique(MaterialTechnique tech, IMaterialProxy* target) +{ + switch (tech) + { + case LifeGraphics::MT_RENDER_FORWARD: + return nullptr; + break; + case LifeGraphics::MT_RENDER_DEFERRED: + { + IMaterialTechnique* technique = new IMaterialTechnique(tech, target); + + + return technique; + + } + break; + default: + return nullptr; + break; + } + return nullptr; +} + +IMaterialPass* IMaterial::CreatePass(IMaterialTechnique* tech, MaterialPass pass) +{ + switch (tech->GetTechniqueType()) + { + case MT_RENDER_FORWARD: + return nullptr; + break; + case MT_RENDER_DEFERRED: + { + switch(pass) + { + case MP_G_FILL: + { + IMaterialPass* p = new IMaterialPass(pass); + String ps, vs; + compiler->CompileNode(graph->GetNodeByID(0), pass, ps, vs, &p->inputInfo); + IShader* psh = ResourceManager::me()->CompileUserShaderPS(ps.c_str(), ps.size()); + IShader* vsh = ResourceManager::me()->CompileUserShaderVS(vs.c_str(), vs.size()); + + + + p->SetShader(ST_PS, psh); + p->SetShader(ST_VS, vsh); + + //set textures + for(int i = 0; i < p->inputInfo.Textures.size(); i++) + { + ShaderTextureMapping mapping = p->inputInfo.Textures[i]; + p->SetTexture(GetTexture(mapping.nodeId), mapping.reg); + } + + return p; + } + case MP_DEPTH_SM: + { + IMaterialPass* p = new IMaterialPass(pass); + String ps, vs; + compiler->CompileNode(graph->GetNodeByID(0), pass, ps, vs, &p->inputInfo); + IShader* psh = ResourceManager::me()->CompileUserShaderPS(ps.c_str(), ps.size()); + IShader* vsh = ResourceManager::me()->CompileUserShaderVS(vs.c_str(), vs.size()); + + p->SetShader(ST_PS, psh); + p->SetShader(ST_VS, vsh); + + return p; + } + default: + return nullptr; + } + } + break; + default: + return nullptr; + break; + } +} +} +/*IMaterial::IMaterial(uint32 constantBufferSize) +:castShadows(false),receiveShadows(false), +ownerFlags(EF_NONE), AlphaBlendEnable(false) +{ +Name = AS_STRING("unnamed"); +materialCb = CG::me()->GetRenderer()->CreateConstantBuffer(sizeof(PER_MATERIAL_CONSTANTS)); +} +IMaterial::~IMaterial() +{ +} +IConstantBuffer* IMaterial::GetConstantBuffer() +{ +return materialCb; +} +void IMaterial::SetTexture(texture_ptr tex, uint32 slot) +{ +textureSlots[slot] = tex; +} +void IMaterial::Apply() +{ +for(int i = 0; i < MAX_TEX_SLOT; i++) +{ +texture_ptr tex = textureSlots[i]; +if(tex.ptr() != 0) +{ +CG::me()->GetRenderer()->SetPSTexture(tex.ptr(), i); +} +} + +//CG::me()->GetRenderer()->SetPixelShader(g_buffer_pass_ps); +//CG::me()->GetRenderer()->SetVertexShader(g_buffer_pass_vs); +}*/ \ No newline at end of file diff --git a/LifeGraphics/render/IMaterial.h b/LifeGraphics/render/IMaterial.h new file mode 100644 index 0000000..585553e --- /dev/null +++ b/LifeGraphics/render/IMaterial.h @@ -0,0 +1,100 @@ +#ifndef IMaterial_h__ +#define IMaterial_h__ +#include "render/IShader.h" +#include "Types.h" +#include "XML.h" +#include "ResourceManager.h" +#include "CG.h" +#include "render/IVideoTexture.h" +#include "shaders/IShaderGraph.h" +#include "shaders/IShaderGraphCompiler.h" +#include +#include + +namespace LifeGraphics +{ +#define MAX_TEX_SLOT 16 + struct TextureSlot + { + texture_ptr Texture; + uint32 SlotID; + }; + + enum MaterialTechnique + { + MT_RENDER_FORWARD, + MT_RENDER_DEFERRED + }; + + enum MaterialPass + { + MP_G_FILL, + MP_DEPTH_SM, + MP_DEPTH_SOLID, + }; + + enum MaterialFlags + { + MF_NONE = 0, + MF_GEN_DEFAULT = 1 << 0, + }; + + class IMaterialPass + { + public: + IMaterialPass(MaterialPass _type); + ~IMaterialPass(); + MaterialPass GetPassType(); + IConstantBuffer* GetConstantBuffer(); + IShader* GetShader(SHADER_TYPE _type); + void SetShader(SHADER_TYPE _type, IShader* shader); + void SetTexture(texture_ptr tex, uint32 slot); + void Apply(); + ShaderInputInfo inputInfo; + private: + MaterialPass passType; + IShader* ps; + IShader* vs; + IShader* gs; + IConstantBuffer* cb; + + texture_ptr textureSlots[MAX_TEX_SLOT]; + }; + + class IMaterialProxy; + class IMaterialTechnique; + class IObjectMesh; + class IMaterial : public IResource + { + friend class IMaterialProxy; + public: + IMaterial(IShaderGraph* _graph, IShaderGraphCompiler* _compiler); + ~IMaterial(); + IMaterialPass* AddPass(MaterialPass _type); + IMaterialPass* GetPass(MaterialPass id); + void SetTexture(int id, texture_ptr tex); + texture_ptr GetTexture(int id); + uint32 GetPassCount(); + bool CastShadows; + bool ReceiveShadows; + bool Opaque; + IMaterialProxy* CreateProxy(IObjectMesh* parent); + MaterialFlags flags; + IMaterialTechnique* CreateTechnique(MaterialTechnique tech, IMaterialProxy* target); + IMaterialPass* CreatePass(IMaterialTechnique* tech, MaterialPass pass); + private: + + + typedef std::map PassCollection; + typedef std::map::iterator PassCollectionItr; + + typedef std::map TexCollection; + typedef std::map::iterator TexCollectionItr; + TexCollection textures; + PassCollection passes; + EFFECT_FLAGS ownerFlags; + IShaderGraph* graph; + IShaderGraphCompiler* compiler; + }; +} +#endif // IMaterial_h__ \ No newline at end of file diff --git a/LifeGraphics/render/IMaterialProxy.cpp b/LifeGraphics/render/IMaterialProxy.cpp new file mode 100644 index 0000000..64874bd --- /dev/null +++ b/LifeGraphics/render/IMaterialProxy.cpp @@ -0,0 +1,92 @@ +#include "render/IMaterialProxy.h" +#include "IObjectMesh.h" + +namespace LifeGraphics +{ +IMaterial* IMaterialProxy::GetBaseMaterial() +{ + return base; +} +bool IMaterialProxy::SelectTechnique(MaterialTechnique tech) +{ + TechniqueCollectionItr it = technics.find(tech); + if(it != technics.end()){ + active_technique = it->second; + return true; + } + else //try to request it from parent material + { + IMaterialTechnique* t = base->CreateTechnique(MT_RENDER_DEFERRED,this); + if(t == nullptr) return false; + active_technique = t; + return AddTechnique(t); + } + return false; +} +IMaterialProxy::IMaterialProxy(IMaterial* source, IObjectMesh* _geom) + :active_technique(nullptr) +{ + base = source; + geom = _geom; +} +bool IMaterialProxy::AddTechnique(IMaterialTechnique* tech) +{ + TechniqueCollectionItr it = technics.find(tech->GetTechniqueType()); + if(it != technics.end())//found + return false; + else + { + technics[tech->GetTechniqueType()] = tech; + return true; + } +} + +IMaterialTechnique* IMaterialProxy::GetTechnique() +{ + return active_technique; +} + +bool IMaterialTechnique::SelectPass(MaterialPass pass) +{ + if(active_pass != NULL && active_pass->GetPassType() == pass) + return true; + PassCollectionItr it = passes.find(pass); + if(it != passes.end()) + { + active_pass = it->second; + return true; + } + + //pass not found -> try to create it! + IMaterialPass* newpass = parent->GetBaseMaterial()->CreatePass(this, pass); + if(newpass != nullptr) + { + active_pass = newpass; + return true; + } + return false; +} +IMaterialPass* IMaterialTechnique::GetPass() +{ + return active_pass; +} +bool IMaterialTechnique::AddPass(IMaterialPass* pass) +{ + PassCollectionItr it = passes.find(pass->GetPassType()); + if(it != passes.end()) //found + return false; + else + { + passes[pass->GetPassType()] = pass; + return true; + } +} +IMaterialTechnique::IMaterialTechnique(MaterialTechnique tech, IMaterialProxy* _parent) + :active_pass(nullptr), tech_type(tech), parent(_parent) +{ +} +MaterialTechnique IMaterialTechnique::GetTechniqueType() +{ + return tech_type; +} +} \ No newline at end of file diff --git a/LifeGraphics/render/IMaterialProxy.h b/LifeGraphics/render/IMaterialProxy.h new file mode 100644 index 0000000..53e672b --- /dev/null +++ b/LifeGraphics/render/IMaterialProxy.h @@ -0,0 +1,50 @@ +#ifndef IMaterialProxy_h__ +#define IMaterialProxy_h__ + +#include "render/IMaterial.h" +#include +#include + +namespace LifeGraphics +{ + class IObjectMesh; + class IMaterialProxy; + class IMaterialTechnique + { + friend class IMaterial; + public: + bool SelectPass(MaterialPass pass); + IMaterialPass* GetPass(); + MaterialTechnique GetTechniqueType(); + private: + IMaterialTechnique(MaterialTechnique tech, IMaterialProxy* _parent); + bool AddPass(IMaterialPass* pass); + typedef std::map PassCollection; + typedef std::map::iterator PassCollectionItr; + PassCollection passes; + IMaterialPass* active_pass; + MaterialTechnique tech_type; + IMaterialProxy* parent; + }; + + class IMaterialProxy + { + friend class IMaterial; + public: + bool SelectTechnique(MaterialTechnique tech); + IMaterialTechnique* GetTechnique(); + IMaterial* GetBaseMaterial(); + private: + bool AddTechnique(IMaterialTechnique* tech); + typedef std::map TechniqueCollection; + typedef std::map::iterator TechniqueCollectionItr; + TechniqueCollection technics; + + IMaterialTechnique* active_technique; + IMaterialProxy(IMaterial* source, IObjectMesh* _geom); + IObjectMesh* geom; + IMaterial* base; + }; +} + +#endif // IMaterialProxy_h__ \ No newline at end of file diff --git a/LifeGraphics/render/IRasterizerState.h b/LifeGraphics/render/IRasterizerState.h new file mode 100644 index 0000000..3f586dc --- /dev/null +++ b/LifeGraphics/render/IRasterizerState.h @@ -0,0 +1,25 @@ +#ifndef IRasterizerState_h__ +#define IRasterizerState_h__ +#include "IResource.h" + +enum FILL_MODE +{ + FILL_WIREFRAME = 2, + FILL_SOLID = 3 +}; + +namespace LifeGraphics +{ + class IRasterizerState : public IResource + { + public: + FILL_MODE Fill; + IRasterizerState() + { + } + virtual ~IRasterizerState() + { + } + }; +} +#endif // IRasterizerState_h__ \ No newline at end of file diff --git a/LifeGraphics/render/IRenderTargetView.h b/LifeGraphics/render/IRenderTargetView.h new file mode 100644 index 0000000..5072712 --- /dev/null +++ b/LifeGraphics/render/IRenderTargetView.h @@ -0,0 +1,13 @@ +#ifndef IRenderTargetView_h__ +#define IRenderTargetView_h__ + +namespace LifeGraphics +{ + class IRenderTargetView + { + public: + IRenderTargetView(){} + virtual ~IRenderTargetView(){} + }; +} +#endif // IRenderTargetView_h__ \ No newline at end of file diff --git a/LifeGraphics/render/IRenderer.cpp b/LifeGraphics/render/IRenderer.cpp new file mode 100644 index 0000000..449ec45 --- /dev/null +++ b/LifeGraphics/render/IRenderer.cpp @@ -0,0 +1,12 @@ +#include "render/IRenderer.h" + +namespace LifeGraphics +{ + IRenderer::IRenderer(){} + + IRenderer::~IRenderer(){} + + HRESULT IRenderer::Open(RenderParams params){return S_OK;} + + void IRenderer::Close(){} +} \ No newline at end of file diff --git a/LifeGraphics/render/IRenderer.h b/LifeGraphics/render/IRenderer.h new file mode 100644 index 0000000..0827170 --- /dev/null +++ b/LifeGraphics/render/IRenderer.h @@ -0,0 +1,199 @@ +#ifndef I_RENDERER +#define I_RENDERER + +#include "render/ITexture2D.h" +#include "render/RenderParams.h" +#include +#include "LString.h" +#include + +#include "render/IVertexBuffer.h" +#include "render/IIndexBuffer.h" +#include "render/IInputLayout.h" +#include "render/IShader.h" +#include "render/IMacroDefine.h" +#include "render/IConstantBuffer.h" +#include "ISwapChain.h" +#include "IMesh.h" +#include "camera/Camera.h" +//#include "render/IMaterial.h" +#include "render/IBlendState.h" +#include "render/IDepthStencilState.h" +#include "Rectangle2D.h" +#include "render/IRenderTargetView.h" +#include "render/ITextureSampler.h" +#include "render/IRasterizerState.h" +#include "Types.h" +#include "render/Enums.h" +#include "DisplayMode.h" +#include "render/ISurface.h" + +namespace LifeGraphics +{ + class IMaterial; + enum PassType + { + PT_RENDER_SHADOW_MAP = 1 << 0, + PT_LIGHTING = 1 << 1, + PT_COMPOSE_FINAL = 1 << 2, + PT_COMPOSE_G_BUF = 1 << 3, + PT_ENV_MAP = 1 << 4, + PT_RENDER_SHADOW_MAP_PSSM = 1 << 5, + }; + + struct PassDesc + { + Camera* camera; + Frustum* frustum; + PassType type; + IMesh* mesh; + uint32 subset; + uint32 flags; + ITexture2D* colorTarget; + ITexture2D* depthTarget; + ITexture2D* colorTarget1; + std::vector objectList; + }; + + class LIFE_EXPORTS IRenderer + { + public: + IRenderer(); + virtual ~IRenderer(); + virtual void BeginCleanup() = 0; + virtual void EndCleanup() = 0; + virtual HRESULT Open(RenderParams params); + virtual void Close(); + //virtual void ClearBuffers(int flags, int rtColor, float zValue, int stencilValue) = 0; + virtual void ClearRenderTarget(ITexture2D* tex, LifeMath::float4* color) = 0; + virtual void ClearDepthStencil(ITexture2D* tex, float zValue, int stencilValue) = 0; + virtual void ClearDepthStencil(ITexture2D* tex, int flag, float zValue, int stencilValue) = 0; + + virtual void ClearRenderTarget(ISurface* target, LifeMath::float4* color) = 0; + virtual void ClearDepthStencil(ISurface* target, int flag, float zValue, int stencilValue) = 0; + + virtual ITexture2D* CreateRenderTarget(int width, int height, int format) = 0; + virtual ITexture2D* CreateRenderTargetCube(int size, int format) = 0; + virtual ITexture2D* CreateDepthTarget(int width, int height, int format) = 0; + virtual ITexture2D* CreateDepthTargetCube(int width, int format) = 0; + virtual const String &GetRendererName() const = 0; + virtual ITexture2D* LoadTexture2D(LifeCore::IDataStreamPtr src) = 0; + virtual ITexture2D* CreateTexture2D(int canRead, int canWrite, int width, int height, int format) = 0; + virtual void RenderToTexture() = 0; + virtual void RenderToTextureNoPs() = 0; + virtual void RenderToTextureNoShader() = 0; + virtual IVertexBuffer* CreateVertexBuffer(int size, void* data, int stride) = 0; + virtual IVertexBuffer* CreateVertexBufferDynamic(int size, int stride) = 0; + + virtual IIndexBuffer* CreateIndexBuffer(int size, void* data, int sixteenBit) = 0; + virtual IIndexBuffer* CreateIndexBufferDynamic(int size, bool sixteenBit) = 0; + virtual IInputLayout* CreateInputLayout(IInputElement* elements, int elementCount, IShader* vertexShader) = 0; + + virtual ITextureSampler* CreateTextureSampler(ISamplerDescription* desc) = 0; + + virtual IShader* CompileVertexShader(const char* pData, int length) = 0; + virtual IShader* CompileVertexShader(LifeCore::IDataStreamPtr stream) = 0; + virtual IShader* CompileVertexShader(LifeCore::IDataStreamPtr stream, std::vector* defines) = 0; + + virtual IShader* CompilePixelShader(const char* pData, int length) = 0; + virtual IShader* CompilePixelShader(LifeCore::IDataStreamPtr stream) = 0; + virtual IShader* CompilePixelShader(LifeCore::IDataStreamPtr stream, std::vector* defines) = 0; + + virtual IConstantBuffer* CreateConstantBuffer(int size) = 0; + + virtual void SetVSConstantBuffer(IConstantBuffer* buffer, int slot) = 0; + virtual void SetPSConstantBuffer(IConstantBuffer* buffer, int slot) = 0; + + virtual void SetInputLayout(IInputLayout* layout) = 0; + virtual void RenderMesh(IMesh* mesh) = 0; + + virtual void SetMaterial(IMaterial* material) = 0; + + virtual void SetTargets(ITexture2D* colorTarget, ITexture2D* depthTarget) = 0; + + virtual void SetTargets(ISurface* colorTarget, ISurface* depthTarget) = 0; + + virtual void SetCubeTarget(ITexture2D* colorTarget, ITexture2D* depthTarget, int faceIndex) = 0; + + virtual void SetTargets(ITexture2D** colorTargets, int targetCount, ITexture2D* depthTarget) = 0; + + virtual void UnbindRenderTargets() = 0; + + virtual void SetColorTarget(ITexture2D* colorTarget) = 0; + virtual void SetDepthTarget(ITexture2D* depthTarget) = 0; + + virtual void SetVertexBuffer(IVertexBuffer* vb) = 0; + virtual void SetIndexBuffer(IIndexBuffer* ib) = 0; + + virtual void SetPrimitiveTopology(PRIMITIVE_TOPOLOGY topology) = 0; + + virtual void SetPSTextureNULL(int slot) = 0; + virtual void DrawIndexed(int offset, int length) = 0; + virtual void DrawIndexed(int offset, int length, int baseVertex) = 0; + + virtual void SetPSTextureSampler(ITextureSampler* sampler, int slot) = 0; + virtual void SetVertexShader(IShader* shader) = 0; + virtual void SetPixelShader(IShader* shader) = 0; + + virtual void SetPSTexture(ITexture2D* texture, int slot) = 0; + virtual void SetPSTextureCube(ITexture2D* texture, int slot) = 0; + virtual void SetPSTexture(ITexture2D* texture, ITexture2D* texture2, int start, int count) = 0; + + virtual void SetVSTexture(ITexture2D* texture, int slot) = 0; + + virtual void UnbindResourcesPS() = 0; + + virtual void Draw(int vertexCount, int vertexOffset) = 0; + + virtual IBlendState* CreateBlendState(bool blendEnable, bool atocEnable, BLEND_OP blendOperation, BLEND_SRC srcBlend, BLEND_SRC dstBlend, uint8 colorMask) = 0; + + virtual void SetBlendState(IBlendState* state) = 0; + + virtual IDepthStencilState* CreateDSState(bool zEnable, bool zWrite, COMPARISON_FUNC zFunc) = 0; + + virtual IDepthStencilState* CreateDSState(bool zEnable, bool zWrite, COMPARISON_FUNC zFunc, bool stencil, int stencilMask, int stencilFunc, int stencilFail, int depthFail, int depthFailBack, int stencilPass) = 0; + + virtual void SetDepthStencilState(IDepthStencilState* state, UINT sref = 0) = 0; + + virtual void SetScissorRectangle(const LifeMath::Rect2i& rect) = 0; + + virtual void SetViewport(const LifeMath::Rect2i& rect) = 0; + + virtual void SetViewport(const LifeMath::Rect2i& rect, float minDepth, float maxDepth) = 0; + + virtual void VerticalBlur(ITexture2D* src, ITexture2D* dst, int numSamples) = 0; + + virtual IShader* CompileUserShaderPS(char* data) = 0; + + virtual IRasterizerState* CreateRasterizerState(FILL_MODE mode) = 0; + virtual IRasterizerState* CreateRasterizerState(FILL_MODE mode, CULL_MODE cmode) = 0; + + virtual void SetRasterizerState(IRasterizerState* state) = 0; + + virtual void SetVertexBuffer(IVertexBuffer** vb, int count) = 0; + + virtual void DrawIndexedInstanced(int offset, int length, int instanceCnt) = 0; + virtual void DrawIndexedInstanced(int offset, int length, int baseVertex, int instanceCnt) = 0; + + virtual void DownSample2x2(ITexture2D* src, ITexture2D* dst, ITexture2D* depth) = 0; + virtual void BlurGaussH(ITexture2D* src, ITexture2D* dst, ITexture2D* depth) = 0; + virtual void BlurGaussV(ITexture2D* src, ITexture2D* dst, ITexture2D* depth) = 0; + virtual int GetDisplayModes(LifeCore::DisplayMode** modeList) = 0; + + virtual ISwapChain* CreateSwapChain(LifeCore::IRenderWindow* window) = 0; + + protected: + RenderParams g_params; + }; + + enum RENDERER_TYPE + { + RT_DIRECTX_10 = 1 << 0, + RT_DIRECTX_9 = 1 << 1, + RT_OPENGL = 1 << 2, + RT_SOFTWARE = 1 << 3, + RT_DIRECTX_11 = 1 << 4, + }; +} + +#endif \ No newline at end of file diff --git a/LifeGraphics/render/IShader.h b/LifeGraphics/render/IShader.h new file mode 100644 index 0000000..de5db3c --- /dev/null +++ b/LifeGraphics/render/IShader.h @@ -0,0 +1,36 @@ +#ifndef IShader_h__ +#define IShader_h__ + +#include "IResource.h" + +namespace LifeGraphics +{ + enum SHADER_TYPE + { + ST_VS = 0, + ST_PS = 1, + ST_GS = 2 + }; + class IShader : public IResource + { + public: + int Flags; + virtual ~IShader() + { + } + //public event ShaderCallback SetConstants; + + //public delegate void ShaderCallback(IObject obj, IShader shader, int subset, ICamera camera); + + // public void OnSetConstants(IObject obj, IShader shader, int subset, ICamera camera) + //{ + //if (SetConstants != null) + //SetConstants(obj, shader, subset, camera); + // } + + virtual void* Disassemble() = 0; + virtual SHADER_TYPE getShaderType() = 0; + virtual char* GetByteCode() = 0; + }; +} +#endif // IShader_h__ \ No newline at end of file diff --git a/LifeGraphics/render/IShaderResourceView.h b/LifeGraphics/render/IShaderResourceView.h new file mode 100644 index 0000000..e50c3bd --- /dev/null +++ b/LifeGraphics/render/IShaderResourceView.h @@ -0,0 +1,17 @@ +#ifndef IShaderResourceView_h__ +#define IShaderResourceView_h__ + +namespace LifeGraphics +{ + class IShaderResourceView + { + public: + IShaderResourceView() + { + } + virtual ~IShaderResourceView() + { + } + }; +} +#endif // IShaderResourceView_h__ \ No newline at end of file diff --git a/LifeGraphics/render/ISurface.h b/LifeGraphics/render/ISurface.h new file mode 100644 index 0000000..dd7a8dc --- /dev/null +++ b/LifeGraphics/render/ISurface.h @@ -0,0 +1,12 @@ +#ifndef ISurface_h__ +#define ISurface_h__ + +namespace LifeGraphics +{ + class ISurface + { + public: + virtual ~ISurface() {} + }; +} +#endif // ISurface_h__ \ No newline at end of file diff --git a/LifeGraphics/render/ITexture2D.h b/LifeGraphics/render/ITexture2D.h new file mode 100644 index 0000000..253ca51 --- /dev/null +++ b/LifeGraphics/render/ITexture2D.h @@ -0,0 +1,37 @@ +#ifndef ITexture2D_h__ +#define ITexture2D_h__ + +#include "IResource.h" +#include "render/ITextureDescription.h" +#include "LString.h" +#include "IDataRectangle.h" +#include "render/IRenderTargetView.h" +#include "render/IShaderResourceView.h" +#include "render/ISurface.h" + +namespace LifeGraphics +{ + class ITexture2D : public IResource + { + public: + int bpp; + virtual ~ITexture2D(){} + ITextureDescription Description; + virtual bool IsVideoTexture() = 0; + virtual int Width() = 0; + virtual int Height() = 0; + virtual LifeCore::IDataRectangle* Map(int level, int read, int write) = 0; + virtual void Unmap(int level){}; + virtual void ToFile(const String& path) {}; + virtual void Load(LifeCore::IDataStream* src) {}; + virtual void CompleteAsyncRequest(IResource* source){}; + virtual IRenderTargetView* getRTView() = 0; + virtual IRenderTargetView* getRTView(int index) = 0; + + virtual IShaderResourceView* getSRView() = 0; + virtual IShaderResourceView* getSRView(int index) = 0; + virtual ISurface* getSurface(int id) = 0; + }; +} + +#endif \ No newline at end of file diff --git a/LifeGraphics/render/ITextureContainer.h b/LifeGraphics/render/ITextureContainer.h new file mode 100644 index 0000000..d29dcbf --- /dev/null +++ b/LifeGraphics/render/ITextureContainer.h @@ -0,0 +1,33 @@ +#ifndef ITextureContainer_h__ +#define ITextureContainer_h__ + +#include "render/ITexture2D.h" +#include + +namespace LifeGraphics +{ + class ITextureContainer + { + private: + void Reset() + { + currTex = frames[0]; + } + std::vector frames; + ITexture2D* currTex; + public: + ITextureContainer() + { + } + ~ITextureContainer() + { + } + void AddFrame(ITexture2D* frame) + { + frames.push_back(frame); + Reset(); + } + ITexture2D* operator->(){return currTex;} + }; +} +#endif // ITextureContainer_h__ \ No newline at end of file diff --git a/LifeGraphics/render/ITextureCube.h b/LifeGraphics/render/ITextureCube.h new file mode 100644 index 0000000..a7b10bd --- /dev/null +++ b/LifeGraphics/render/ITextureCube.h @@ -0,0 +1,19 @@ +#ifndef ITextureCube_h__ +#define ITextureCube_h__ + +#include "render/ITexture2D.h" + +namespace LifeGraphics +{ + class ITexture2DArray : public ITexture2D + { + public: + ITexture2DArray() + { + } + ~ITexture2DArray() + { + } + }; +} +#endif // ITextureCube_h__ \ No newline at end of file diff --git a/LifeGraphics/render/ITextureDescription.h b/LifeGraphics/render/ITextureDescription.h new file mode 100644 index 0000000..0f9098e --- /dev/null +++ b/LifeGraphics/render/ITextureDescription.h @@ -0,0 +1,13 @@ +#ifndef ITextureDescription_h__ +#define ITextureDescription_h__ + +namespace LifeGraphics +{ + typedef struct ITextureDescription + { + int Width, Height; + int Format; + int SampleCount, SampleQuality; + }ITextureDescription; +} +#endif \ No newline at end of file diff --git a/LifeGraphics/render/ITextureSampler.h b/LifeGraphics/render/ITextureSampler.h new file mode 100644 index 0000000..77978fa --- /dev/null +++ b/LifeGraphics/render/ITextureSampler.h @@ -0,0 +1,79 @@ +#ifndef ITextureSampler_h__ +#define ITextureSampler_h__ +#include "IResource.h" +#include "render/Enums.h" +namespace LifeGraphics +{ + enum TextureFilter + { + MinMagMipPoint = 0, + MinMagPointMipLinear = 1, + MinPointMagLinearMipPoint = 4, + MinPointMagMipLinear = 5, + MinLinearMagMipPoint = 16, + MinLinearMagPointMipLinear = 17, + MinMagLinearMipPoint = 20, + MinMagMipLinear = 21, + Anisotropic = 85, + ComparisonMinMagMipPoint = 128, + ComparisonMinMagPointMipLinear = 129, + ComparisonMinPointMagLinearMipPoint = 132, + ComparisonMinPointMagMipLinear = 133, + ComparisonMinLinearMagMipPoint = 144, + ComparisonMinLinearMagPointMipLinear = 145, + ComparisonMinMagLinearMipPoint = 148, + ComparisonMinMagMipLinear = 149, + ComparisonAnisotropic = 213, + }; + + enum TextureAddressMode + { + Wrap = 1, + Mirror = 2, + Clamp = 3, + Border = 4, + MirrorOnce = 5, + }; + + struct ISamplerDescription + { + TextureAddressMode AddressU; + TextureAddressMode AddressV; + TextureAddressMode AddressW; + TextureFilter Filter; + COMPARISON_FUNC CmpFunc; + + ISamplerDescription(TextureAddressMode _aU, TextureAddressMode _aV, TextureAddressMode _aW, TextureFilter _f) + { + AddressU = _aU; + AddressV = _aV; + AddressW = _aW; + Filter = _f; + CmpFunc = COMPARISON_ALWAYS; + } + ISamplerDescription(TextureAddressMode _aU, TextureAddressMode _aV, TextureAddressMode _aW, TextureFilter _f, COMPARISON_FUNC cmp) + { + AddressU = _aU; + AddressV = _aV; + AddressW = _aW; + Filter = _f; + CmpFunc = cmp; + } + ISamplerDescription() + { + } + }; + + class ITextureSampler : public IResource + { + public: + ISamplerDescription Description; + ITextureSampler() + { + } + virtual ~ITextureSampler() + { + } + }; +} +#endif // ITextureSampler_h__ \ No newline at end of file diff --git a/LifeGraphics/render/IVertexBuffer.h b/LifeGraphics/render/IVertexBuffer.h new file mode 100644 index 0000000..0ac6ede --- /dev/null +++ b/LifeGraphics/render/IVertexBuffer.h @@ -0,0 +1,20 @@ +#ifndef I_VERTEX_BUFFER_H__ +#define I_VERTEX_BUFFER_H__ + +#include "IDataBuffer.h" + +namespace LifeGraphics +{ + class IVertexBuffer : public IDataBuffer + { + public: + virtual ~IVertexBuffer(){} + unsigned int stride; + int vertexCount; + virtual void Update(void* data, size_t length) + { + } + }; +} + +#endif \ No newline at end of file diff --git a/LifeGraphics/render/IVideoTexture.h b/LifeGraphics/render/IVideoTexture.h new file mode 100644 index 0000000..bdf53dc --- /dev/null +++ b/LifeGraphics/render/IVideoTexture.h @@ -0,0 +1,88 @@ +#ifndef IVideoTexture_h__ +#define IVideoTexture_h__ + +#include "render/ITexture2D.h" +#include + +namespace LifeGraphics +{ + class IVideoTexture : public LifeGraphics::ITexture2D + { + public: + float fps; + float time; + IVideoTexture() + :fps(24.0f), time(0.0f) + { + } + ~IVideoTexture() + { + } + ISurface* getSurface(int id) + { + return currTex->getSurface(id); + } + void AddFrame(ITexture2D* frame) + { + frames.push_back(frame); + currTex = frames[0]; + } + ITexture2D* GetPtr() + { + return currTex; + } + //ITexture2D* operator->(){return currTex;} + + LifeCore::IDataRectangle* Map(int level, int read, int write) + { + return 0; + } + void Update(float dt) + { + time+=dt; + int cnt = frames.size(); + float tpf = 1.0f / fps; + int cur = (int)(time / tpf); //-V2003 + cur = cur%cnt; + SelectFrame(cur); + } + void SelectFrame(int frame) + { + currTex = frames[frame]; + } + int Width() + { + return currTex->Width(); + } + int Height() + { + return currTex->Height(); + } + bool IsVideoTexture() + { + return true; + } + IRenderTargetView* getRTView() + { + return currTex->getRTView(); + } + IRenderTargetView* getRTView(int index) + { + return currTex->getRTView(index); + } + + IShaderResourceView* getSRView() + { + return currTex->getSRView(); + } + IShaderResourceView* getSRView(int index) + { + return currTex->getSRView(index); + } + + private: + std::vector frames; + ITexture2D* currTex; + }; +} +#endif // IVideoTexture_h__ \ No newline at end of file diff --git a/LifeGraphics/render/PostDOF.h b/LifeGraphics/render/PostDOF.h new file mode 100644 index 0000000..e0dd2ea --- /dev/null +++ b/LifeGraphics/render/PostDOF.h @@ -0,0 +1,86 @@ +#ifndef PostDOF_h__ +#define PostDOF_h__ + +#include "render/PostEffect.h" +#include "Globals.h" +#include "render/ITextureSampler.h" +#include "math/LifeMath.h" +#include "LifeDebug.h" + +namespace LifeGraphics +{ + class PostDOF : public PostEffect + { + private: + IRenderer* renderer; + IShader* psDof; + IBlendState* blendState; + IBlendState* blendStateOpaque; + IDepthStencilState* depthState; + public: + ITexture2D* dofBlur1; + ITexture2D* dofBlur2; + ITexture2D* dofBlur3; + ITexture2D* dofBlur4; + //ITexture2D* dofResult; + PostDOF() + { + } + ITexture2D* Execute(PostEffectParams& params) + { + renderer->SetBlendState(blendStateOpaque); + renderer->SetDepthStencilState(depthState); + + renderer->UnbindResourcesPS(); + renderer->UnbindRenderTargets(); + LifeMath::float4 color(1.0f, 0.0f, 0.0f, 1.0f); + renderer->ClearRenderTarget(dofBlur1, &color); + renderer->DownSample2x2(params.renderTarget, dofBlur1, params.depthTarget); + + renderer->UnbindResourcesPS(); + renderer->UnbindRenderTargets(); + renderer->DownSample2x2(dofBlur1, dofBlur2, params.depthTarget); + + renderer->UnbindResourcesPS(); + renderer->UnbindRenderTargets(); + renderer->DownSample2x2(dofBlur2, dofBlur3, params.depthTarget); + + renderer->UnbindResourcesPS(); + renderer->UnbindRenderTargets(); + renderer->DownSample2x2(dofBlur3, dofBlur4, params.depthTarget); + + renderer->UnbindResourcesPS(); + renderer->UnbindRenderTargets(); + LifeMath::Rect2i vp = LifeMath::Rect2i(0,0,params.renderTarget->Width(), params.renderTarget->Height()); + renderer->SetScissorRectangle(vp); + renderer->SetViewport(vp); + renderer->SetPixelShader(psDof); + renderer->SetPSTexture(params.depthTex, 1); + renderer->SetPSTexture(dofBlur4, 2); + renderer->SetTargets(params.renderTarget, params.depthTarget); + renderer->SetBlendState(blendState); + renderer->SetDepthStencilState(depthState); + renderer->RenderToTextureNoPs(); + + return dofBlur1; + } + void Init(SceneDescriptor& desc) + { + renderer = CG::me()->GetRenderer(); + + dofBlur1 = renderer->CreateRenderTarget(desc.width / 2, desc.height / 2, FORMAT_R16G16B16A16_FLOAT); + dofBlur2 = renderer->CreateRenderTarget(desc.width / 4, desc.height / 4, FORMAT_R16G16B16A16_FLOAT); + dofBlur3 = renderer->CreateRenderTarget(desc.width / 8, desc.height / 8, FORMAT_R16G16B16A16_FLOAT); + dofBlur4 = renderer->CreateRenderTarget(desc.width / 16, desc.height / 16, FORMAT_R16G16B16A16_FLOAT); + //dofResult = renderer->CreateRenderTarget(desc.width, desc.height); + // + psDof = ResourceManager::me()->CompileUserShaderPS(STR("PostDOF.psh")); + + blendState = renderer->CreateBlendState(true, false, BLEND_OP_ADD, BLEND_SRC_ALPHA, BLEND_INV_SRC_ALPHA, 1 | 2 | 4 | 8); + blendStateOpaque = renderer->CreateBlendState(false, false, BLEND_OP_ADD, BLEND_SRC_ALPHA, BLEND_INV_SRC_ALPHA, 1 | 2 | 4 | 8); + depthState = renderer->CreateDSState(false, false, COMPARISON_NEVER); + } + }; +} + +#endif // PostDOF_h__ \ No newline at end of file diff --git a/LifeGraphics/render/PostEffect.h b/LifeGraphics/render/PostEffect.h new file mode 100644 index 0000000..7a5cbad --- /dev/null +++ b/LifeGraphics/render/PostEffect.h @@ -0,0 +1,30 @@ +#ifndef PostEffect_h__ +#define PostEffect_h__ +#include "ResourceManager.h" + +namespace LifeGraphics +{ + struct SceneDescriptor + { + int width; + int height; + }; + + struct PostEffectParams + { + ITexture2D* renderTarget; + ITexture2D* depthTarget; + ITexture2D* depthTex; + float timeElapsed; + }; + + class PostEffect + { + public: + PostEffect(){} + virtual ~PostEffect(){} + virtual ITexture2D* Execute(PostEffectParams& params) = 0; + virtual void Init(SceneDescriptor& desc) = 0; + }; +} +#endif // PostEffect_h__ \ No newline at end of file diff --git a/LifeGraphics/render/PostHDR.h b/LifeGraphics/render/PostHDR.h new file mode 100644 index 0000000..5ad6c2e --- /dev/null +++ b/LifeGraphics/render/PostHDR.h @@ -0,0 +1,420 @@ +#ifndef PostHDR_h__ +#define PostHDR_h__ +#include "render/PostEffect.h" +#include "Globals.h" +#include "render/ITextureSampler.h" +#include "math/LifeMath.h" + +#define _CRTDBG_MAP_ALLOC + +#include "LifeDebug.h" +#include +#include + + +namespace LifeGraphics +{ + class PostHDR : public PostEffect + { + public: + ITexture2D* g_brightPassTexture; + ITexture2D* g_downSampleTexture; + ITexture2D* g_horizontalBlur; + ITexture2D* g_verticalBlur; + ITexture2D* g_finalImage; + ITexture2D* g_lumLast; + ITexture2D* g_lumCur; + static const int NumLuminanceTextures = 4; + ITexture2D* luminanceTextures[NumLuminanceTextures]; + ITexture2D* luminanceTexturesZ[NumLuminanceTextures]; + ITexture2D* src; + ITexture2D* srcZ; + + ITexture2D* downSampleZ; + ITexture2D* brightPassZ; + + ITextureSampler* samplerPointClamp; + + float exposure; + float brightTheresold; + + PostHDR() + { + } + ~PostHDR() + { + SAFE_DELETE(g_brightPassTexture); + SAFE_DELETE(g_downSampleTexture); + SAFE_DELETE(g_horizontalBlur); + SAFE_DELETE(g_verticalBlur); + SAFE_DELETE(g_finalImage); + SAFE_DELETE(g_lumLast); + SAFE_DELETE(g_lumCur); + SAFE_DELETE(downSampleZ); + SAFE_DELETE(brightPassZ); + + SAFE_DELETE(g_downSamplePS); + SAFE_DELETE(g_brightPassPS); + SAFE_DELETE(g_horizontalBlurPS); + SAFE_DELETE(g_verticalBlurPS); + SAFE_DELETE(g_downSample2x2PS); + SAFE_DELETE(g_downSample3x3PS); + SAFE_DELETE(g_resampleAvgLumExp); + SAFE_DELETE(g_lumAdaptation); + SAFE_DELETE(g_composePS); + SAFE_DELETE(g_constBuffer); + SAFE_DELETE(g_pointSampler); + SAFE_DELETE(g_linearSampler); + SAFE_DELETE(g_cbLumAdaptation); + + for(int i = 0; iSetPSTextureSampler(g_pointSampler, 0); + g_renderer->SetPSTextureSampler(g_linearSampler, 1); + + g_renderer->SetTargets(g_downSampleTexture, downSampleZ); + g_renderer->SetViewport(LifeMath::Rect2i(0, 0, g_downSampleTexture->Width(), g_downSampleTexture->Height())); + + //1'st pass - do down sampling + g_renderer->SetPixelShader(g_downSamplePS); + g_renderer->SetPSTexture(params.renderTarget, 0); + src = params.renderTarget; + srcZ = params.depthTarget; + + Get4x4Offsets(params.renderTarget->Width(), params.renderTarget->Height(), offsets2); + g_constBuffer->Update(offsets2, sizeof(LifeMath::float4)*16); + g_renderer->SetPSConstantBuffer(g_constBuffer, 0); + // g_downSamplePS.SetFloat4A("downSampleOffsets", offsets2); + g_renderer->RenderToTextureNoPs(); + + // g_downSampleTexture.ToFile("C:\\rt_1_downSample.jpg"); + + //2'nd measure scene luminance + MeasureLuminance(g_downSampleTexture, params.depthTex); + //3'rd calculate scene luminance adaptation + // + CalculateLuminanceAdaptation(params.timeElapsed); + + //4'th step - bright pass + g_renderer->UnbindResourcesPS(); + g_renderer->SetTargets(g_brightPassTexture, brightPassZ); + g_renderer->SetPixelShader(g_brightPassPS); + g_renderer->SetPSTexture(g_downSampleTexture, 0); + g_renderer->SetPSTexture(g_lumCur, 1); + g_renderer->SetPSTexture(params.depthTex, 2); + //g_renderer.SetTextureSampler(g_pointSampler, 0); + /// g_renderer.SetTextureSampler(g_pointSampler, 1); + g_renderer->SetViewport(LifeMath::Rect2i(0, 0, g_brightPassTexture->Width(), g_brightPassTexture->Height())); + g_renderer->SetScissorRectangle(LifeMath::Rect2i(0, 0, g_brightPassTexture->Width(), g_brightPassTexture->Height())); + + g_renderer->RenderToTextureNoPs(); + + // g_brightPassTexture.ToFile("C:\\rt_2_brightPass.jpg"); + // g_renderer.SetTextureSampler(g_linearSampler, 0); + + g_renderer->UnbindResourcesPS(); + + g_renderer->SetPSTextureSampler(samplerPointClamp, 0); + g_renderer->SetPSTextureSampler(samplerPointClamp, 1); + //5'th pass - do horizontal blur + g_renderer->SetTargets(g_horizontalBlur, downSampleZ); + g_renderer->SetPixelShader(g_horizontalBlurPS); + g_renderer->SetPSTexture(g_brightPassTexture, 0); + + GetBlurWeightedOffsets((float)NumBlurSamples, g_brightPassTexture->Width(), offsets3); + g_constBuffer->Update(offsets3, sizeof(LifeMath::float4)*NumBlurSamples); + g_renderer->SetPSConstantBuffer(g_constBuffer, 0); + // g_horizontalBlurPS.SetFloat4A("hBlurOffsets", offsets3); + g_renderer->SetViewport(LifeMath::Rect2i(0, 0, g_horizontalBlur->Width(), g_horizontalBlur->Height())); + g_renderer->SetScissorRectangle(LifeMath::Rect2i(0, 0, g_horizontalBlur->Width(), g_horizontalBlur->Height())); + + g_renderer->RenderToTextureNoPs(); + + // g_horizontalBlur.ToFile("C:\\rt_3_horBlur.jpg"); + //6'th pass - do vertical blur + + g_renderer->UnbindResourcesPS(); + g_renderer->SetTargets(g_verticalBlur, downSampleZ); + g_renderer->SetPixelShader(g_verticalBlurPS); + g_renderer->SetPSTexture(g_horizontalBlur, 0); + + GetBlurWeightedOffsets((float)NumBlurSamples, g_brightPassTexture->Height(), offsets3); + //g_verticalBlurPS.SetFloat4A("vBlurOffsets", offsets4); + + g_constBuffer->Update(offsets3, sizeof(LifeMath::float4)*NumBlurSamples); + g_renderer->SetPSConstantBuffer(g_constBuffer, 0); + + g_renderer->RenderToTextureNoPs(); + + //7'th compose final image + ComposeFinalImage(params.renderTarget, g_lumCur, g_verticalBlur); + // g_finalImage.ToFile("C:\\rt_5_final.jpg"); + return g_finalImage; + } + void Init(SceneDescriptor& desc) + { + g_renderer = CG::me()->GetRenderer(); + + g_lumAdaptation = ResourceManager::me()->CompileUserShaderPS(String(_text("HDRLumAdaptation.psh"))); + + /* char laPs[] = + "Texture2D tex0 : register(t7);"\ + "Texture2D tex1 : register(t8);"\ + "SamplerState linearSampler : register(s0);"\ + "cbuffer timeData : register(b11){float4 g_fElapsedTime : packoffset(c0);};"\ + "float4 main_PS(in float4 Position : SV_Position, in float2 texcoord : TEXCOORD0) : SV_Target{"\ + "float4 fAdaptedLum =tex0.Sample(linearSampler, float2(0.25f, 0.25f));"\ + "float4 fCurrentLum = tex1.Sample(linearSampler, float2(0.5f, 0.5f));"\ + "float fNewAdaptation = fAdaptedLum + (fCurrentLum - fAdaptedLum) * g_fElapsedTime.x;"\ + "return fAdaptedLum;}"; + + g_lumAdaptation = g_renderer->CompileUserShaderPS(laPs);*/ + + ISamplerDescription pscDesc(Clamp, Clamp, Clamp, MinMagMipPoint); + samplerPointClamp = g_renderer->CreateTextureSampler(&pscDesc); + + exposure = 0.72f; + brightTheresold = 0.7f; + + g_finalImage = g_renderer->CreateRenderTarget(desc.width, desc.height, FORMAT_R16G16B16A16_FLOAT); + + //Bright pass texture + int brightPassTextureWidth = desc.width / 4; + int brightPassTextureHeight = desc.height / 4; + g_brightPassTexture = g_renderer->CreateRenderTarget(brightPassTextureWidth, + brightPassTextureHeight, FORMAT_R16G16B16A16_FLOAT); + + brightPassZ = g_renderer->CreateDepthTarget(brightPassTextureWidth, + brightPassTextureHeight, FORMAT_D16_UNORM); + + //Down sample texture + int downSampledTextureWidth = brightPassTextureWidth / 1; + int downSampledTextureHeight = brightPassTextureHeight / 1; + g_downSampleTexture = g_renderer->CreateRenderTarget(downSampledTextureWidth, + downSampledTextureHeight, FORMAT_R16G16B16A16_FLOAT); + + downSampleZ = g_renderer->CreateDepthTarget(downSampledTextureWidth, + downSampledTextureHeight, FORMAT_D16_UNORM); + + //Horizontal blur texture + int horizontalBlurTextureWidth = downSampledTextureWidth; + int horizontalBlurTextureHeight = downSampledTextureHeight; + g_horizontalBlur = g_renderer->CreateRenderTarget(horizontalBlurTextureWidth, + horizontalBlurTextureHeight, FORMAT_R16G16B16A16_FLOAT); + + //Vertical blur texture + int verticalBlurTextureWidth = horizontalBlurTextureWidth; + int verticalBlurTextureHeight = horizontalBlurTextureHeight; + g_verticalBlur = g_renderer->CreateRenderTarget(verticalBlurTextureWidth, + verticalBlurTextureHeight, FORMAT_R16G16B16A16_FLOAT); + + g_downSamplePS = ResourceManager::me()->CompileUserShaderPS(String(_text("DownSample.psh"))); + g_brightPassPS = ResourceManager::me()->CompileUserShaderPS(String(_text("BrightPass.psh"))); + g_horizontalBlurPS = ResourceManager::me()->CompileUserShaderPS(String(_text("HorizontalBlur.psh"))); + + g_verticalBlurPS = ResourceManager::me()->CompileUserShaderPS(String(_text("VerticalBlur.psh"))); + + g_resampleAvgLumExp = ResourceManager::me()->CompileUserShaderPS(String(_text("ResampleAvgLumExp.psh"))); + + //LC + g_downSample2x2PS = ResourceManager::me()->CompileUserShaderPS(String(_text("SampleLumInitial.psh"))); + g_downSample3x3PS = ResourceManager::me()->CompileUserShaderPS(String(_text("DownSampleLum3.psh"))); + g_composePS = ResourceManager::me()->CompileUserShaderPS(String(_text("ComposeHDR.psh"))); + + g_constBuffer = g_renderer->CreateConstantBuffer(16 * 16*256); + + g_cbLumAdaptation = g_renderer->CreateConstantBuffer(sizeof(LifeMath::float4)); + + CreateLuminanceTextures(); + + ISamplerDescription psDesc(Wrap, Wrap, Wrap, MinMagMipPoint); + ISamplerDescription lsDesc(Wrap, Wrap, Wrap, Anisotropic); + + g_pointSampler = g_renderer->CreateTextureSampler(&psDesc); + g_linearSampler = g_renderer->CreateTextureSampler(&lsDesc); + } + private: + static const int MAX_SAMPLES = 6; + + //post effects + IShader* g_downSamplePS; + IShader* g_brightPassPS; + IShader* g_horizontalBlurPS; + IShader* g_verticalBlurPS; + + //luminance calculation + IShader* g_downSample2x2PS; + IShader* g_downSample3x3PS; + IShader* g_resampleAvgLumExp; + IShader* g_lumAdaptation; + + //final composing shader + IShader* g_composePS; + + static const int NumBlurSamples = 9; + + //luminance calculator + + IRenderer* g_renderer; + + IConstantBuffer* g_constBuffer; + ITextureSampler* g_pointSampler; + ITextureSampler* g_linearSampler; + + IConstantBuffer* g_cbLumAdaptation; + + LifeMath::float4 offsets2[16]; + LifeMath::float4 offsets3[NumBlurSamples]; + LifeMath::float4 offsets[9]; + + void CreateLuminanceTextures() + { + for (int i = 0; i < NumLuminanceTextures; i++) + { + int iSampleLen = 1 << (2 * i); + luminanceTextures[i] = g_renderer->CreateRenderTarget(iSampleLen, iSampleLen, FORMAT_R16G16B16A16_FLOAT); + luminanceTexturesZ[i] = g_renderer->CreateDepthTarget(iSampleLen, iSampleLen, FORMAT_D16_UNORM); + } + + g_lumCur = g_renderer->CreateRenderTarget(1, 1, FORMAT_R16G16B16A16_FLOAT); + g_lumLast = g_renderer->CreateRenderTarget(1, 1, FORMAT_R16G16B16A16_FLOAT); + + + LifeMath::float4 clear_color(1,1,1,1); + g_renderer->ClearRenderTarget(g_lumCur, &clear_color); + g_renderer->ClearRenderTarget(g_lumLast, &clear_color); + } + + void Render2x2Pass(ITexture2D* srcTexture, ITexture2D* destTexture, ITexture2D* sceneDepth) + { + float tU, tV; + tU = 1.0f / (3.0f * destTexture->Width()); + tV = 1.0f / (3.0f * destTexture->Height()); + + int index = 0; + for (int x = -1; x <= 1; x++) + { + for (int y = -1; y <= 1; y++) + { + offsets[index].X = (float)x * tU; + offsets[index].Y = (float)y * tV; + + index++; + } + } + + g_renderer->SetTargets(destTexture,luminanceTexturesZ[NumLuminanceTextures - 1]); + g_renderer->SetPSTexture(srcTexture, 0); + g_renderer->SetPSTexture(sceneDepth, 1); + g_renderer->SetPixelShader(g_downSample2x2PS); + + g_constBuffer->Update(&offsets, sizeof(LifeMath::float4)* 9); + g_renderer->SetPSConstantBuffer(g_constBuffer, 0); + + // With everything configured we can now render the first pass to the luminance textures. + g_renderer->SetViewport(LifeMath::Rect2i(0, 0, destTexture->Width(), destTexture->Height())); + + g_renderer->RenderToTextureNoPs(); + } + void MeasureLuminance(ITexture2D* hdrTexture, ITexture2D* depth) + { + int curTex = NumLuminanceTextures - 1; + Render2x2Pass(hdrTexture, luminanceTextures[curTex],depth); + + curTex--; + while (curTex > 0) + { + g_renderer->SetTargets(luminanceTextures[curTex], luminanceTexturesZ[curTex]); + Get4x4Offsets(luminanceTextures[curTex + 1]->Width(), luminanceTextures[curTex + 1]->Height(), offsets2); + + g_renderer->SetPixelShader(g_downSample3x3PS); + g_renderer->SetPSTexture(luminanceTextures[curTex + 1], 0); + + g_constBuffer->Update(offsets2, sizeof(LifeMath::float4)*16); + g_renderer->SetPSConstantBuffer(g_constBuffer, 0); + + g_renderer->SetViewport(LifeMath::Rect2i(0, 0, luminanceTextures[curTex]->Width(), luminanceTextures[curTex]->Height())); + g_renderer->SetScissorRectangle(LifeMath::Rect2i(0, 0, luminanceTextures[curTex]->Width(), luminanceTextures[curTex]->Height())); + g_renderer->RenderToTextureNoPs(); + curTex--; + } + + g_renderer->SetTargets(luminanceTextures[0],luminanceTexturesZ[0]); + g_renderer->SetPixelShader(g_resampleAvgLumExp); + Get4x4Offsets(luminanceTextures[1]->Width(), luminanceTextures[1]->Height(), offsets2); + g_constBuffer->Update(offsets2, sizeof(LifeMath::float4)*16); + g_renderer->SetPSConstantBuffer(g_constBuffer, 0); + + g_renderer->SetPSTexture(luminanceTextures[1], 0); + g_renderer->SetViewport(LifeMath::Rect2i(0, 0, luminanceTextures[0]->Width(), luminanceTextures[0]->Height())); + g_renderer->SetScissorRectangle(LifeMath::Rect2i(0, 0, luminanceTextures[0]->Width(), luminanceTextures[0]->Height())); + + g_renderer->RenderToTextureNoPs(); + } + void CalculateLuminanceAdaptation(float timeElapsed) + { + g_renderer->SetViewport(LifeMath::Rect2i(0, 0, 1, 1)); + g_renderer->SetScissorRectangle(LifeMath::Rect2i(0, 0, 1, 1)); + + g_renderer->UnbindResourcesPS(); + + ITexture2D* texSwap = g_lumLast; + g_lumLast = g_lumCur; + g_lumCur = texSwap; + + g_renderer->SetTargets(g_lumCur, luminanceTexturesZ[0]); + LifeMath::float4 clear_color(0,0,0,0); + g_renderer->ClearRenderTarget(g_lumCur, &clear_color); + g_renderer->SetPixelShader(g_lumAdaptation); + + g_renderer->SetPSTexture(g_lumLast, luminanceTextures[0], 7,2); + // g_renderer->SetPSTexture(rgba.ptr(), luminanceTextures[0], 7,2); + + if(timeElapsed <0.0000001f) + timeElapsed = 0.0000001f; + + if(timeElapsed > 1.0f) + timeElapsed = 1.0f; + + float val = (1.0f - (pow(0.98f, 30.0f * timeElapsed))); + LifeMath::float4 time(val, 1.0f, 1.0f, 1.0f); + + g_cbLumAdaptation->Update(&time, sizeof(LifeMath::float4)); + g_renderer->SetPSConstantBuffer(g_cbLumAdaptation, 11); + + g_renderer->RenderToTextureNoPs(); + } + void ComposeFinalImage(ITexture2D* hdrTexture, ITexture2D* luminanceTexture, ITexture2D* bloomTexture) + { + g_renderer->SetPSTextureSampler(g_linearSampler, 1); + g_renderer->SetTargets(g_finalImage, srcZ); + + g_renderer->SetPixelShader(g_composePS); + g_renderer->SetPSTexture(hdrTexture, 0); + g_renderer->SetPSTexture(bloomTexture, 1); + g_renderer->SetPSTextureNULL(2); + g_renderer->SetPSTexture(luminanceTexture, 3); + + float g_fKeyValue = 0.05f; + LifeMath::float4 key(g_fKeyValue, 0, 0, 0); + g_constBuffer->Update(key, sizeof(LifeMath::float4)); + g_renderer->SetPSConstantBuffer(g_constBuffer, 0); + // g_composePS.SetFloat("g_fMiddleGray", g_fKeyValue); + + g_renderer->SetViewport(LifeMath::Rect2i(0, 0, g_finalImage->Width(), g_finalImage->Height())); + + g_renderer->SetScissorRectangle(LifeMath::Rect2i(0, 0, g_finalImage->Width(), g_finalImage->Height())); + + g_renderer->RenderToTextureNoPs(); + + g_renderer->UnbindResourcesPS(); + } + }; +} +#endif // PostHDR_h__ \ No newline at end of file diff --git a/LifeGraphics/render/PostSSAO.cpp b/LifeGraphics/render/PostSSAO.cpp new file mode 100644 index 0000000..2e76909 --- /dev/null +++ b/LifeGraphics/render/PostSSAO.cpp @@ -0,0 +1,101 @@ +#include "render/PostSSAO.h" +#include "IScene.h" +#include "ResourceManager.h" + +namespace LifeGraphics +{ + struct QUAD_VERT + { + LifeMath::float4 position; + LifeMath::float2 texcoord; + }; + + PostSSAO::PostSSAO(IScene* scene, QUALITY effectQuality) + { + pScene = scene; + pRenderer = CG::me()->GetRenderer(); + + ssaoSize = pScene->getOutputSize(); + + int divider = 1; + + //adjust ssao texture size by quality + switch(effectQuality) + { + case Q_MED: + divider = 2; + break; + case Q_LOW: + divider = 4; + break; + } + + ssaoSize /= divider; + + ssaoTex = pRenderer->CreateRenderTarget(ssaoSize.X, ssaoSize.Y, FORMAT_R8_UNORM); + ssaoBlur = pRenderer->CreateRenderTarget(ssaoSize.X, ssaoSize.Y, FORMAT_R8_UNORM); + + psRender = ResourceManager::me()->CompileUserShaderPS(String(_text("SSAO.psh"))); + vs = ResourceManager::me()->CompileUserShaderVS(String(_text("SSAO.vsh"))); + psBlur = ResourceManager::me()->CompileUserShaderPS(String(_text("SSAO_BLUR.psh"))); + + ISamplerDescription pscDesc(Clamp, Clamp, Clamp, MinMagMipPoint); + ISamplerDescription lsDesc(Wrap, Wrap, Wrap, MinMagMipLinear); + + linearSampler = CG::me()->GetRenderer()->CreateTextureSampler(&lsDesc); + pointSamplerClamp = CG::me()->GetRenderer()->CreateTextureSampler(&pscDesc); + + depthState = pRenderer->CreateDSState(false, false, COMPARISON_NEVER); + + ssaoRandom = ResourceManager::me()->GetTexture(String(_text("root/Data/Textures/ssao_random.bmp"))); + } + void PostSSAO::process(IConstantBuffer* camera, ITexture2D* sceneGbuf, ITexture2D* sceneDepthTarget) + { + pRenderer->UnbindRenderTargets(); + + //pRenderer->SetPSConstantBuffer(cbGlobal, 0); + //pRenderer->SetVSConstantBuffer(cbGlobal, 0); + + pRenderer->SetPSTextureSampler(pointSamplerClamp, 0); + pRenderer->SetPSTextureSampler(linearSampler, 1); + pRenderer->SetVSConstantBuffer(camera, 1); + pRenderer->SetPSConstantBuffer(camera, 1); + pRenderer->SetViewport(LifeMath::Rect2i(0, 0, ssaoSize.X, ssaoSize.Y)); + + pRenderer->SetDepthStencilState(depthState); + + // renderer->SetBlendState(lppBlend); + pRenderer->SetVertexShader(vs); + pRenderer->SetPixelShader(psRender); + + //pRenderer->ClearRenderTarget(ssaoTex, &color ); + pRenderer->SetTargets(ssaoTex, sceneDepthTarget); + pRenderer->SetPSTexture(sceneGbuf, 0); + pRenderer->SetPSTexture(ssaoRandom.ptr(), 1); + + pRenderer->RenderToTextureNoShader(); + + pRenderer->SetPSTextureSampler(linearSampler, 0); + pRenderer->SetPSTextureSampler(pointSamplerClamp, 1); + + //blur ssao + pRenderer->UnbindRenderTargets(); + pRenderer->SetTargets(ssaoBlur, sceneDepthTarget); + + pRenderer->SetPSTexture(ssaoTex, 0); + pRenderer->SetPSTexture(sceneGbuf, 9); + pRenderer->SetPixelShader(psBlur); + pRenderer->RenderToTextureNoShader(); + } + ITexture2D* PostSSAO::getTexture() + { + return ssaoBlur; + } + PostSSAO::~PostSSAO() + { + vs->Release(); + psBlur->Release(); + psRender->Release(); + ssaoTex->Release(); + } +} \ No newline at end of file diff --git a/LifeGraphics/render/PostSSAO.h b/LifeGraphics/render/PostSSAO.h new file mode 100644 index 0000000..90f0621 --- /dev/null +++ b/LifeGraphics/render/PostSSAO.h @@ -0,0 +1,39 @@ +#ifndef PostSSAO_h__ +#define PostSSAO_h__ + +#include "render/IRenderer.h" +#include "Globals.h" +#include "math/LifeMath.h" + + +namespace LifeGraphics +{ + class IScene; + class PostSSAO + { + private: + IRenderer* pRenderer; + IScene* pScene; + IShader* vs; + IShader* psRender; + IShader* psBlur; + ITexture2D* ssaoTex; + ITexture2D* ssaoBlur; + texture_ptr ssaoRandom; + ITextureSampler* pointSamplerClamp; + IDepthStencilState* depthState; + ITextureSampler* linearSampler; + LifeMath::Vec2i ssaoSize; + public: + LifeMath::Vec2i getSize() + { + return ssaoSize; + } + ITexture2D* getTexture(); + void process(IConstantBuffer* camera, ITexture2D* sceneGbuf, ITexture2D* sceneDepthTarget); + PostSSAO(IScene* scene, QUALITY effectQuality); + ~PostSSAO(); + }; +} + +#endif // PostSSAO_h__ \ No newline at end of file diff --git a/LifeGraphics/render/PosteffectManager.h b/LifeGraphics/render/PosteffectManager.h new file mode 100644 index 0000000..dae11c6 --- /dev/null +++ b/LifeGraphics/render/PosteffectManager.h @@ -0,0 +1,56 @@ +#ifndef PosteffectManager_h__ +#define PosteffectManager_h__ +#include +#include "render/PostEffect.h" + +namespace LifeGraphics +{ + class PostEffectManager + { + private: + std::vector postEffects; + SceneDescriptor descriptor; + public: + PostEffectManager() + { + } + ~PostEffectManager() + { + } + void Init(SceneDescriptor& desc) + { + descriptor = desc; + } + + void Add(PostEffect* effect) + { + postEffects.push_back(effect); + } + + ITexture2D* Execute(ITexture2D* source, ITexture2D* depth, ITexture2D* depthTex, float dt) + { + ITexture2D* tmpResult = (ITexture2D*)0; + + for (UINT i = 0; i < postEffects.size(); i++) + { + PostEffectParams params; + params.timeElapsed = dt; + params.depthTarget = depth; + params.depthTex = depthTex; + + if (tmpResult) + { + params.renderTarget = tmpResult; + tmpResult = postEffects[i]->Execute(params); + } + else + { + params.renderTarget = source; + tmpResult = postEffects[i]->Execute(params); + } + } + return tmpResult; + } + }; +} +#endif // PosteffectManager_h__ \ No newline at end of file diff --git a/LifeGraphics/render/RenderParams.h b/LifeGraphics/render/RenderParams.h new file mode 100644 index 0000000..2a0dc1d --- /dev/null +++ b/LifeGraphics/render/RenderParams.h @@ -0,0 +1,22 @@ +#ifndef RENDERER_PARAMETERS +#define RENDERER_PARAMETERS +#include "Build.h" +#include + +namespace LifeGraphics +{ + class LIFE_EXPORTS RenderParams + { + public: + RenderParams(){} + ~RenderParams(){} + bool ForceWideScreen; + bool AllowSwitchToSoftware; + RenderParams(bool allowSoftware, bool fwdScrn) + { + AllowSwitchToSoftware = allowSoftware; + ForceWideScreen = fwdScrn; + } + }; +} +#endif \ No newline at end of file diff --git a/LifeGraphics/render/RendererDX10.cpp b/LifeGraphics/render/RendererDX10.cpp new file mode 100644 index 0000000..e8601ad --- /dev/null +++ b/LifeGraphics/render/RendererDX10.cpp @@ -0,0 +1,80 @@ +/* +#pragma once +#include "render/RendererDX10.h" +#include +#include +namespace Video +{ +HRESULT RendererDX10::Open(RenderParams params) +{ + HRESULT hr = S_OK; + g_params = params; + + DXGI_SWAP_CHAIN_DESC sd; + ZeroMemory(&sd, sizeof(sd)); + + //fill swap chain description parameters + sd.BufferCount = 1; + sd.BufferDesc.Width = g_params.ScreenWidth; + sd.BufferDesc.Height = g_params.ScreenHeight; + sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + sd.BufferDesc.RefreshRate.Numerator = 60; + sd.BufferDesc.RefreshRate.Denominator = 1; + sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + sd.OutputWindow = (HWND)params.WindowHandle; + sd.Windowed = TRUE; + sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + sd.SampleDesc.Count = 1; + sd.SampleDesc.Quality = 0; + + D3D10_DRIVER_TYPE drivers[]= + { + D3D10_DRIVER_TYPE_HARDWARE, + D3D10_DRIVER_TYPE_WARP + }; + int numDrivers = sizeof(drivers) / sizeof(drivers[0]); + + for(int i = 0; i< numDrivers; i++) + { + g_driverType = drivers[i]; + hr = D3D10CreateDeviceAndSwapChain(NULL,g_driverType,NULL,D3D10_CREATE_DEVICE_DEBUG, D3D10_SDK_VERSION, &sd, &g_pSwapChain, &g_pDevice); + + if(SUCCEEDED(hr)) + break; + } + + if(FAILED(hr)) + return hr; + + ID3D10Texture2D* pBuffer; + hr = g_pSwapChain->GetBuffer(0, __uuidof(ID3D10Texture2D), (void**)&pBuffer); + if(FAILED(hr)) + return hr; + + hr = g_pDevice->CreateRenderTargetView(pBuffer, NULL, &g_pBackBuffer); + if(FAILED(hr)) + return hr; + + g_pDevice->OMSetRenderTargets(0, &g_pBackBuffer, NULL); + + return S_OK; +} + +void RendererDX10::Close() +{ + if(g_pDevice) + { + float clearColor[4] = {1.0f, 1.0f,0.0f,1.0f}; + g_pDevice->ClearRenderTargetView(g_pBackBuffer, clearColor); + + g_pSwapChain->Present(0,0); + } +} + +void RendererDX10::ClearBuffers(int flags, int rtColor, float zValue, float stencilValue) +{ + + +} +} +*/ diff --git a/LifeGraphics/render/RendererDX10.h b/LifeGraphics/render/RendererDX10.h new file mode 100644 index 0000000..c1b9c9e --- /dev/null +++ b/LifeGraphics/render/RendererDX10.h @@ -0,0 +1,10 @@ +#ifndef RENDERER_D3D10 +#define RENDERER_D3D10 + + + +namespace Video +{ + +} +#endif \ No newline at end of file diff --git a/LifeGraphics/render/RendererSoftware.h b/LifeGraphics/render/RendererSoftware.h new file mode 100644 index 0000000..4361492 --- /dev/null +++ b/LifeGraphics/render/RendererSoftware.h @@ -0,0 +1,183 @@ +#ifndef RendererSoftware_h__ +#define RendererSoftware_h__ + +#include "Globals.h" +#include "render/IRenderer.h" +#include "math/LifeMath.h" +#include +#include "render/software/ITexture2DS.h" +#include "render/software/IVertexBufferS.h" +#include "render/software/IIndexBufferS.h" + +#include "render/RenderParams.h" +#include +#include "render/software/IVertexShaderS.h" +#include "render/software/IPixelShaderS.h" +#include "render/software/IInputLayoutS.h" +#include "render/software/IBlendStateS.h" +#include "math/LifeMath.h" +#include "render/software/IRasterizerStateS.h" +#include "render/software/ITextureSamplerS.h" + +namespace LifeGraphics +{ + class RendererSoftware : public IRenderer + { + public: + RendererSoftware(){}; + ~RendererSoftware(){printf("Destroying renderer...");Close();}; + virtual HRESULT Open(RenderParams params); + virtual void Close(); + virtual void ClearBuffers(int flags, int rtColor, float zValue, int stencilValue); + virtual const std::string &GetRendererName() const { + static String sName("LifeEngine software graphics driver"); + return sName; + } + ITexture2D* CreateRenderTarget(int width, int height, int format); + ITexture2D* CreateDepthTarget(int width, int height, int format); + ITexture2D* LoadTexture2D(LifeCore::IDataStream* src); + ITexture2D* CreateTexture2D(int canRead, int canWrite, int width, int height, int format); + void RenderToTexture(); + void RenderToTextureNoPs(); + void RenderToTextureNoShader(); + IVertexBuffer* CreateVertexBuffer(int size, void* data, int stride); + IVertexBuffer* CreateVertexBufferDynamic(int size, int stride); + IIndexBuffer* CreateIndexBuffer(int size, void* data, int sixteenBit); + IIndexBuffer* CreateIndexBufferDynamic(int size, bool sixteenBit); + IInputLayout* CreateInputLayout(IInputElement* elements, int elementCount, IShader* vertexShader); + + IShader* CompileVertexShader(LifeCore::IDataStream* stream); + IShader* CompileVertexShader(LifeCore::IDataStream* stream, std::vector* defines); + + IShader* CompilePixelShader(LifeCore::IDataStream* stream); + IShader* CompilePixelShader(LifeCore::IDataStream* stream, std::vector* defines); + + IConstantBuffer* CreateConstantBuffer(int size); + + void SetVSConstantBuffer(IConstantBuffer* buffer, int slot); + void SetPSConstantBuffer(IConstantBuffer* buffer, int slot); + + void SetInputLayout(IInputLayout* layout); + + void SwapBuffers(); + void RenderMesh(IMesh* mesh); + + void SetCamera(Camera* camera); + void SetMaterial(IMaterial* material); + + void SetTargets(ITexture2D* colorTarget, ITexture2D* depthTarget); + + void SetDefaultTargets(); + + void SetVertexBuffer(IVertexBuffer* vb); + void SetIndexBuffer(IIndexBuffer* ib); + + void SetPrimitiveTopology(PRIMITIVE_TOPOLOGY topology); + + void DrawIndexed(int offset, int length); + + void SetVertexShader(IShader* shader); + void SetPixelShader(IShader* shader); + + void SetPSTexture(ITexture2D* texture, int slot); + void SetVSTexture(ITexture2D* texture, int slot); + + void UnbindResourcesPS(); + + void Draw(int vertexCount, int vertexOffset); + + IBlendState* CreateBlendState(bool blendEnable, bool atocEnable, BLEND_OP blendOperation, BLEND_SRC srcBlend, BLEND_SRC dstBlend, uint8 colorMask); + + void SetBlendState(IBlendState* state); + + IDepthStencilState* CreateDSState(bool zEnable, bool zWrite, COMPARISON_FUNC zFunc); + + void SetDepthStencilState(IDepthStencilState* state); + + void SetScissorRectangle(const Rectangle2D& rect); + + void SetViewport(const Rectangle2D& rect); + + ITexture2D* CreateRenderTargetCube(int size, int format); + + ITexture2D* CreateDepthTargetCube(int width, int format); + + void SetPSTextureNULL(int slot); + + void SetPSTextureSampler(ITextureSampler* sampler, int slot); + + ITextureSampler* CreateTextureSampler(ISamplerDescription* desc); + + void SetColorTarget(ITexture2D* colorTarget); + void SetDepthTarget(ITexture2D* depthTarget); + + void VerticalBlur(ITexture2D* src, ITexture2D* dst, int numSamples); + + void SetPSTexture(ITexture2D* texture, ITexture2D* texture2, int start, int count); + + IShader* CompileUserShaderPS(char* data); + + IRasterizerState* CreateRasterizerState(FILL_MODE mode); + + void DrawIndexed(int offset, int length, int baseVertex); + + void SetRasterizerState(IRasterizerState* state); + + void SetTargets(ITexture2D** colorTargets, int targetCount, ITexture2D* depthTarget); + + void UnbindRenderTargets(); + + void SetVertexBuffer(IVertexBuffer** vb, int count); + + void DrawIndexedInstanced(int offset, int length, int instanceCnt); + + void DrawIndexedInstanced(int offset, int length, int baseVertex, int instanceCnt); + + void SetViewport(const Rectangle2D& rect, float minDepth, float maxDepth); + + IRasterizerState* CreateRasterizerState(FILL_MODE mode, CULL_MODE cmode); + + void BeginCleanup(); + void EndCleanup(); + + void ClearRenderTarget(ITexture2D* tex, LifeMath::float4* color); + void ClearDepthStencil(ITexture2D* tex, float zValue, int stencilValue); + + int GetDisplayModes(LifeCore::DisplayMode** modeList); + + private: + struct SimpleVertex + { + LifeMath::float4 Pos; + LifeMath::float2 TC; + }; + //ID3D11RasterizerState* rState; + // ID3D11Device* g_pDevice; + //ID3D11DeviceContext* g_pDeviceContext; + //IDXGISwapChain* g_pSwapChain; + //ID3D11RenderTargetView* g_pBackBuffer; + //ID3D11DepthStencilView* g_pDepthBuffer; + + // ID3D11RenderTargetView* currentBackBuffer; + // ID3D11DepthStencilView* currentDepthBuffer; + + //D3D_FEATURE_LEVEL g_pFeatureLevel; + //D3D_DRIVER_TYPE g_driverType; + LifeMath::float4x4 wvp; + //ID3D11Buffer* fsQuadBuffer; + //ID3D11PixelShader* psQuad; + //ID3D11VertexShader* vsQuad; + //ID3D11InputLayout* fsQuadLayout; + //ID3D11SamplerState* samplerLinear; + //ID3D11SamplerState* samplerPoint; + //ID3D11Texture2D* pBuffer; + //ID3D11Texture2D* dBuf; + Camera* cam; + + //ID3D11PixelShader* psVerticalBlur; + + IConstantBuffer* vbBuffer; + }; +} + +#endif // RendererSoftware_h__ \ No newline at end of file diff --git a/LifeGraphics/render/dx11/IBlendState11.h b/LifeGraphics/render/dx11/IBlendState11.h new file mode 100644 index 0000000..13d1368 --- /dev/null +++ b/LifeGraphics/render/dx11/IBlendState11.h @@ -0,0 +1,33 @@ +#ifndef IBlendState11_h__ +#define IBlendState11_h__ + +#include "render/IBlendState.h" + +#include +#include +#include + +namespace LifeGraphics +{ + class IBlendState11 : public IBlendState + { + private: + ID3D11Device* device; + ID3D11DeviceContext* context; + + public: + ID3D11BlendState* state; + IBlendState11(ID3D11Device* _device, ID3D11BlendState* _state) + { + device = _device; + device->GetImmediateContext(&context); + state = _state; + } + ~IBlendState11() + { + SAFE_RELEASE(state); + } + }; +} + +#endif // IBlendState11_h__ \ No newline at end of file diff --git a/LifeGraphics/render/dx11/IConstantBuffer11.cpp b/LifeGraphics/render/dx11/IConstantBuffer11.cpp new file mode 100644 index 0000000..f0ce440 --- /dev/null +++ b/LifeGraphics/render/dx11/IConstantBuffer11.cpp @@ -0,0 +1,12 @@ +#include "render/dx11/IConstantBuffer11.h" +#include + +namespace LifeGraphics +{ + void IConstantBuffer11::Update(void* pData, int dataLength) + { + context->Map(buffer,0, D3D11_MAP_WRITE_DISCARD, 0, &subresource); + memcpy(subresource.pData, pData, dataLength); + context->Unmap(buffer, 0); + } +} \ No newline at end of file diff --git a/LifeGraphics/render/dx11/IConstantBuffer11.h b/LifeGraphics/render/dx11/IConstantBuffer11.h new file mode 100644 index 0000000..9b676f2 --- /dev/null +++ b/LifeGraphics/render/dx11/IConstantBuffer11.h @@ -0,0 +1,61 @@ +#ifndef IConstantBuffer11_h__ +#define IConstantBuffer11_h__ + +#include "render/IConstantBuffer.h" + +#include +#include +#include +#include "Globals.h" +#include + +namespace LifeGraphics +{ + class IConstantBuffer11 : public IConstantBuffer + { + public: + ID3D11Buffer* buffer; + ID3D11Device* device; + ID3D11DeviceContext* context; + int size; + D3D11_MAPPED_SUBRESOURCE subresource; + + IConstantBuffer11(ID3D11Device* _device, int _size) + { + if((_size % 0x10) !=0 ) + { + LOG_ERROR("Buffer size must be multiple of 16", "IConstantBuffer11", "ctor()"); + return; + } + device = _device; + size = _size; + + D3D11_BUFFER_DESC desc = {0}; + desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + desc.ByteWidth = _size; + desc.Usage = D3D11_USAGE_DYNAMIC; + + HRESULT hr = device->CreateBuffer(&desc,NULL, &buffer); + + device->GetImmediateContext(&context); + /* + BufferDescription desc = new BufferDescription(); + desc.BindFlags = BindFlags.ConstantBuffer; + desc.CpuAccessFlags = CpuAccessFlags.Write; + desc.OptionFlags = ResourceOptionFlags.None; + desc.SizeInBytes = size; + desc.StructureByteStride = 0; + desc.Usage = D3D11.ResourceUsage.Dynamic; + + buffer = new D3D11.Buffer(device, desc);*/ + } + ~IConstantBuffer11() + { + SAFE_RELEASE(buffer); + context = NULL; + } + void Update(void* pData, int dataLength); + }; +} +#endif // IConstantBuffer11_h__ \ No newline at end of file diff --git a/LifeGraphics/render/dx11/IDepthStencilState11.h b/LifeGraphics/render/dx11/IDepthStencilState11.h new file mode 100644 index 0000000..5185130 --- /dev/null +++ b/LifeGraphics/render/dx11/IDepthStencilState11.h @@ -0,0 +1,31 @@ +#ifndef IDepthStencilState11_h__ +#define IDepthStencilState11_h__ + +#include "render/IDepthStencilState.h" + +#include +#include +#include + +namespace LifeGraphics +{ + class IDepthStencilState11 : public IDepthStencilState + { + private: + ID3D11Device* device; + ID3D11DeviceContext* context; + public: + ID3D11DepthStencilState* state; + IDepthStencilState11(ID3D11Device* _device, ID3D11DepthStencilState* _state) + { + device = _device; + device->GetImmediateContext(&context); + state = _state; + } + ~IDepthStencilState11() + { + SAFE_RELEASE(state); + } + }; +} +#endif // IDepthStencilState11_h__ \ No newline at end of file diff --git a/LifeGraphics/render/dx11/IIndexBuffer11.h b/LifeGraphics/render/dx11/IIndexBuffer11.h new file mode 100644 index 0000000..2eb1cb9 --- /dev/null +++ b/LifeGraphics/render/dx11/IIndexBuffer11.h @@ -0,0 +1,98 @@ +#ifndef I_INDEX_BUFFER11_H__ +#define I_INDEX_BUFFER11_H__ + +#include "render/IIndexBuffer.h" +#include "Globals.h" +#include "ResourceManager.h" +#include "IMemoryStream.h" + + +#include +#include +#include + +namespace LifeGraphics +{ + class IIndexBuffer11 : public IIndexBuffer + { + public: + ID3D11Buffer* ib; + ID3D11Device* device; + ID3D11DeviceContext* context; + D3D11_MAPPED_SUBRESOURCE* mappedData; + LifeCore::IMemoryStream stm; + + ~IIndexBuffer11() + { + SAFE_RELEASE(ib); + } + IIndexBuffer11(ID3D11Device* _device, void* pBuffer, int _size, int _sixteenBit, int staging) + { + device = _device; + length = _size; + isShort = _sixteenBit; + + //g_desc = new BufferDescription(size, D3D11.ResourceUsage.Default, + // BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); + D3D11_BUFFER_DESC bd; + ZeroMemory( &bd, sizeof(bd) ); + if(pBuffer) + bd.Usage = D3D11_USAGE_DEFAULT; + else + bd.Usage = D3D11_USAGE_DYNAMIC; + if(staging) + bd.Usage = D3D11_USAGE_STAGING; + + bd.ByteWidth = _size; + bd.BindFlags = D3D11_BIND_INDEX_BUFFER; + + if(bd.Usage == D3D11_USAGE_DEFAULT) + bd.CPUAccessFlags = 0; + else if(bd.Usage == D3D11_USAGE_DYNAMIC) + bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + else if(bd.Usage == D3D11_USAGE_STAGING) + bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE | D3D11_CPU_ACCESS_READ; + + if(pBuffer) + { + D3D11_SUBRESOURCE_DATA InitData; + ZeroMemory( &InitData, sizeof(InitData) ); + InitData.pSysMem = pBuffer; + + device->CreateBuffer( &bd, &InitData, &ib ); + } + else + device->CreateBuffer( &bd, NULL, &ib ); + device->GetImmediateContext(&context); + //if( FAILED( hr ) ) + // return hr; + } + LifeCore::IDataStream* Map(int offset, int flags) + { + D3D11_MAP mapFlags; + + if (flags & LF_Discard) + mapFlags = D3D11_MAP_WRITE_DISCARD; + else if ((flags & LF_NoOverwrite)) + mapFlags = D3D11_MAP_WRITE_NO_OVERWRITE; + + else if ((flags & LF_ReadOnly)) + mapFlags = D3D11_MAP_READ; + + else if (flags == LF_None) + mapFlags = D3D11_MAP_READ_WRITE; + else + mapFlags = D3D11_MAP_WRITE; + + context->Map(ib,0, mapFlags,0, mappedData); + stm.Reset(((char*)mappedData->pData) + offset, length); + return &stm; + } + void Unmap() + { + context->Unmap(ib, 0); + } + }; +} + +#endif \ No newline at end of file diff --git a/LifeGraphics/render/dx11/IInputLayout11.h b/LifeGraphics/render/dx11/IInputLayout11.h new file mode 100644 index 0000000..475a14c --- /dev/null +++ b/LifeGraphics/render/dx11/IInputLayout11.h @@ -0,0 +1,23 @@ +#ifndef IInputLayout11_h__ +#define IInputLayout11_h__ + +#include "render/IInputLayout.h" +#include "Globals.h" +#include +#include +#include + +namespace LifeGraphics +{ + class IInputLayout11 : public IInputLayout + { + public: + ID3D11InputLayout* Decl; + IInputLayout11(){} + ~IInputLayout11() + { + SAFE_RELEASE(Decl); + } + }; +} +#endif // IInputLayout11_h__ \ No newline at end of file diff --git a/LifeGraphics/render/dx11/IPixelShader11.h b/LifeGraphics/render/dx11/IPixelShader11.h new file mode 100644 index 0000000..2138713 --- /dev/null +++ b/LifeGraphics/render/dx11/IPixelShader11.h @@ -0,0 +1,109 @@ +#ifndef IPixelShader11_h__ +#define IPixelShader11_h__ + +#include +#include +#include +#include "render/IShader.h" +#include "render/IMacroDefine.h" +#include +#include "Globals.h" +#include "D3Dcompiler.h" + +namespace LifeGraphics +{ + class IPixelShader11 : public IShader + { + public: + ID3D11PixelShader* g_shader; + ID3DBlob* g_bytecode; + ID3D11Device* g_device; + IPixelShader11(ID3D11Device* device) + { + g_device = device; + } + ~IPixelShader11() + { + SAFE_RELEASE(g_bytecode); + SAFE_RELEASE(g_shader); + } + void* Disassemble() + { + /*ID3D10Blob* errorMsg = NULL; + D3D10DisassembleShader(g_bytecode->GetBufferPointer(), g_bytecode->GetBufferSize(),TRUE, "OLOLO", &errorMsg); + return errorMsg->GetBufferPointer();*/ + return 0; + } + SHADER_TYPE getShaderType() + { + return ST_PS; + } + char* GetByteCode() + { + return (char*)0; + } + void Compile(LifeCore::IDataStreamPtr stream, std::vector* defines) + { + char* shader = new char[stream->Length()]; + stream->Read(shader, stream->Length()); + Compile(shader, stream->Length(), defines); + delete[]shader; + } + void Compile(const char* pData, int length, std::vector* defines) + { + DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS; +#ifdef _DEBUG + dwShaderFlags |= D3DCOMPILE_DEBUG; +#endif + + HRESULT hr; + g_bytecode = NULL; + + // char* ololo = new char[10*1024*1024]; + // void* lol = malloc(10*1024*1024); + ID3D10Blob* errorMsg = NULL; + + if(!defines) + { + hr = D3DX11CompileFromMemory(pData, length, "system_shader", NULL, NULL, "main_PS", "ps_5_0",dwShaderFlags,0,NULL, &g_bytecode,&errorMsg, NULL); + } + else + { + //create macro + D3D10_SHADER_MACRO* macros = new D3D10_SHADER_MACRO[defines->size() + 1]; + int cnt = defines->size(); + for(int i = 0; i < cnt; i++) + { + macros[i].Definition = defines[0][i].definition.c_str(); + macros[i].Name = defines[0][i].name.c_str(); + } + D3D10_SHADER_MACRO m; + m.Definition = NULL; + m.Name = NULL; + macros[cnt] = m; + + hr = D3DX11CompileFromMemory(pData, length, "system_shader", macros, NULL, "main_PS", "ps_5_0",dwShaderFlags,0,NULL, &g_bytecode, &errorMsg, NULL); + delete[] macros; + } + + if(hr != S_OK) + { + printf("SHADER COMPILE ERROR: %s", errorMsg->GetBufferPointer()); + } + SAFE_RELEASE(errorMsg); + + hr = g_device->CreatePixelShader(g_bytecode->GetBufferPointer(), g_bytecode->GetBufferSize(), NULL, &g_shader); + SAFE_RELEASE(g_bytecode); + } + void Load(LifeCore::IDataStreamPtr src) + { + printf("Load VS is not implemented: %i\n", src->Length()); + } + void CompleteAsyncRequest(IResource* source) + { + // printf("VS async not implemented: %s\n", source->Name.c_str()); + } + }; +} + +#endif // IPixelShader11_h__ \ No newline at end of file diff --git a/LifeGraphics/render/dx11/IRasterizerState11.h b/LifeGraphics/render/dx11/IRasterizerState11.h new file mode 100644 index 0000000..0bfb62d --- /dev/null +++ b/LifeGraphics/render/dx11/IRasterizerState11.h @@ -0,0 +1,62 @@ +#ifndef IRasterizerState11_h__ +#define IRasterizerState11_h__ + +#include +#include +#include +#include "render/IRasterizerState.h" +#include "Globals.h" + +namespace LifeGraphics +{ + class IRasterizerState11 : public IRasterizerState + { + public: + ID3D11Device* device; + ID3D11RasterizerState* state; + + IRasterizerState11(FILL_MODE mode, ID3D11Device* _device) + { + device = _device; + + D3D11_RASTERIZER_DESC rsDesc; + rsDesc.AntialiasedLineEnable = false; + rsDesc.CullMode = D3D11_CULL_BACK; + rsDesc.DepthBias = 0; + rsDesc.DepthBiasClamp = 0.0f; + rsDesc.SlopeScaledDepthBias = 0.0f; + rsDesc.DepthClipEnable = TRUE; + rsDesc.FillMode = static_cast(mode); + rsDesc.FrontCounterClockwise = FALSE; + rsDesc.MultisampleEnable = FALSE; + rsDesc.ScissorEnable = TRUE; + + device->CreateRasterizerState(&rsDesc, &state); + } + IRasterizerState11(FILL_MODE mode, ID3D11Device* _device, CULL_MODE cmode) + { + device = _device; + + D3D11_RASTERIZER_DESC rsDesc; + rsDesc.AntialiasedLineEnable = false; + rsDesc.CullMode = static_cast(cmode); + rsDesc.DepthBias = 0; + rsDesc.DepthBiasClamp = 0.0f; + rsDesc.SlopeScaledDepthBias = 0.0f; + rsDesc.DepthClipEnable = TRUE; + rsDesc.FillMode = static_cast(mode); + rsDesc.FrontCounterClockwise = FALSE; + rsDesc.MultisampleEnable = FALSE; + rsDesc.ScissorEnable = TRUE; + + device->CreateRasterizerState(&rsDesc, &state); + } + ~IRasterizerState11() + { + SAFE_RELEASE(state); + } + }; +} + + +#endif // IRasterizerState11_h__ \ No newline at end of file diff --git a/LifeGraphics/render/dx11/IRenderTargetViewDX11.h b/LifeGraphics/render/dx11/IRenderTargetViewDX11.h new file mode 100644 index 0000000..50dec2c --- /dev/null +++ b/LifeGraphics/render/dx11/IRenderTargetViewDX11.h @@ -0,0 +1,27 @@ +#ifndef IRenderTargetViewDX11_h__ +#define IRenderTargetViewDX11_h__ +#include "render/IRenderTargetView.h" + +#include +#include +#include +#include "Globals.h" + +namespace LifeGraphics +{ + class IRenderTargetView11 : public IRenderTargetView + { + public: + ID3D11RenderTargetView* view; + + IRenderTargetView11(ID3D11RenderTargetView* _view) + { + view = _view; + } + ~IRenderTargetView11() + { + SAFE_RELEASE(view); + } + }; +} +#endif // IRenderTargetViewDX11_h__ \ No newline at end of file diff --git a/LifeGraphics/render/dx11/IShaderResourceView11.h b/LifeGraphics/render/dx11/IShaderResourceView11.h new file mode 100644 index 0000000..b19bbf1 --- /dev/null +++ b/LifeGraphics/render/dx11/IShaderResourceView11.h @@ -0,0 +1,26 @@ +#ifndef IShaderResourceView11_h__ +#define IShaderResourceView11_h__ + +#include "../IShaderResourceView.h" +#include +#include +#include +#include "Globals.h" + +namespace LifeGraphics +{ + class IShaderResourceView11 : public IShaderResourceView + { + public: + ID3D11ShaderResourceView* view; + IShaderResourceView11(ID3D11ShaderResourceView* _view) + { + view = _view; + } + ~IShaderResourceView11() + { + SAFE_RELEASE(view); + } + }; +} +#endif // IShaderResourceView11_h__ \ No newline at end of file diff --git a/LifeGraphics/render/dx11/ISurface11.cpp b/LifeGraphics/render/dx11/ISurface11.cpp new file mode 100644 index 0000000..3365913 --- /dev/null +++ b/LifeGraphics/render/dx11/ISurface11.cpp @@ -0,0 +1,33 @@ +#include "render/dx11/ISurface11.h" + +namespace LifeGraphics +{ + ISurface11::ISurface11() + { + } + void ISurface11::setRTV(ID3D11RenderTargetView* _rtv) + { + rtv = _rtv; + } + void ISurface11::setSRV(ID3D11ShaderResourceView* _srv) + { + srv = _srv; + } + void ISurface11::setDSV(ID3D11DepthStencilView* _dsv) + { + dsv = _dsv; + } + + ID3D11RenderTargetView* ISurface11::getRTV() + { + return rtv; + } + ID3D11ShaderResourceView* ISurface11::getSRV() + { + return srv; + } + ID3D11DepthStencilView* ISurface11::getDSV() + { + return dsv; + } +} \ No newline at end of file diff --git a/LifeGraphics/render/dx11/ISurface11.h b/LifeGraphics/render/dx11/ISurface11.h new file mode 100644 index 0000000..66e4995 --- /dev/null +++ b/LifeGraphics/render/dx11/ISurface11.h @@ -0,0 +1,31 @@ +#ifndef ISurface11_h__ +#define ISurface11_h__ + +#include "render/ISurface.h" +#include "render/dx11/IRenderTargetViewDX11.h" +#include "render/dx11/IShaderResourceView11.h" + +#include +#include +#include + +namespace LifeGraphics +{ + class ISurface11 : public ISurface + { + public: + ISurface11(); + void setRTV(ID3D11RenderTargetView* _rtv); + void setSRV(ID3D11ShaderResourceView* _srv); + void setDSV(ID3D11DepthStencilView* _dsv); + + ID3D11RenderTargetView* getRTV(); + ID3D11ShaderResourceView* getSRV(); + ID3D11DepthStencilView* getDSV(); + protected: + ID3D11RenderTargetView* rtv; + ID3D11ShaderResourceView* srv; + ID3D11DepthStencilView* dsv; + }; +} +#endif // ISurface11_h__ \ No newline at end of file diff --git a/LifeGraphics/render/dx11/ISwapChain11.cpp b/LifeGraphics/render/dx11/ISwapChain11.cpp new file mode 100644 index 0000000..1f01332 --- /dev/null +++ b/LifeGraphics/render/dx11/ISwapChain11.cpp @@ -0,0 +1,85 @@ +#include "render/dx11/ISwapChain11.h" +#include +#include "render/dx11/ITexture2D11.h" + +namespace LifeGraphics +{ + ISwapChain11::ISwapChain11(ID3D11Device* device, LifeCore::IRenderWindow* wnd){ + IDXGIDevice* dxgi_dev; + IDXGIAdapter* dxgi_adapter; + IDXGIFactory* gxgi_factory; + + DXGI_SWAP_CHAIN_DESC sd; + ZeroMemory(&sd, sizeof(sd)); + + //fill swap chain description parameters + sd.BufferCount = 1; + sd.BufferDesc.Width = wnd->GetWidth(); + sd.BufferDesc.Height = wnd->GetHeight(); + sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + + sd.BufferDesc.RefreshRate.Numerator = 60; + sd.BufferDesc.RefreshRate.Denominator = 1; + sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + sd.OutputWindow = (HWND)wnd->GetWindowHandle(); + sd.Windowed = true; + sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + sd.SampleDesc.Count = 1; + sd.SampleDesc.Quality = 0; + + device->QueryInterface(__uuidof(IDXGIDevice), (void**)(&dxgi_dev)); + dxgi_dev->GetParent(__uuidof(IDXGIAdapter), (void**)(&dxgi_adapter)); + dxgi_adapter->GetParent(__uuidof(IDXGIFactory), (void**)(&gxgi_factory)); + // dxgi_adapter->EnumOutputs(0, &pDXGIOutput); + gxgi_factory->CreateSwapChain(device, &sd, &swap_chain); + gxgi_factory->MakeWindowAssociation((HWND)wnd->GetWindowHandle(), DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_WINDOW_CHANGES); + + ID3D11Texture2D* back_buffer_raw; + ID3D11RenderTargetView* back_buffer_rtv; + swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&back_buffer_raw); + device->CreateRenderTargetView(back_buffer_raw, NULL, &back_buffer_rtv); + + + + back_buffer = new LifeGraphics::ITexture2D11(device); + back_buffer->FromTexture(back_buffer_raw); + back_buffer->addRenderTargetView(new LifeGraphics::IRenderTargetView11(back_buffer_rtv)); + + + D3D11_TEXTURE2D_DESC dDesc = {0}; + dDesc.ArraySize = 1; + dDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL; + dDesc.CPUAccessFlags = 0; + dDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; + dDesc.MipLevels = 1; + dDesc.SampleDesc.Count = 1; + dDesc.SampleDesc.Quality = 0; + dDesc.Usage = D3D11_USAGE_DEFAULT; + dDesc.Width = wnd->GetWidth(); + dDesc.Height = wnd->GetHeight(); + + ID3D11Texture2D* depth_buffer_raw; + ID3D11DepthStencilView* depth_buffer_raw_dsv; + device->CreateTexture2D(&dDesc, NULL, &depth_buffer_raw); + device->CreateDepthStencilView(depth_buffer_raw,NULL, &depth_buffer_raw_dsv); + + depth_buffer = new LifeGraphics::ITexture2D11(device); + depth_buffer->FromTexture(depth_buffer_raw); + depth_buffer->addDepthStencilView(depth_buffer_raw_dsv); + } + LifeGraphics::ITexture2D* ISwapChain11::GetBackBuffer(uint8 id){ + return static_cast(back_buffer); + } + LifeGraphics::ITexture2D* ISwapChain11::GetDepthBuffer(uint8 id){ + return static_cast(depth_buffer); + } + void ISwapChain11::Resize(uint16 width, uint16 height){ + + } + void ISwapChain11::Present(){ + swap_chain->Present(0,0); + } + ISwapChain11::~ISwapChain11(){ + + } +} \ No newline at end of file diff --git a/LifeGraphics/render/dx11/ISwapChain11.h b/LifeGraphics/render/dx11/ISwapChain11.h new file mode 100644 index 0000000..9af6def --- /dev/null +++ b/LifeGraphics/render/dx11/ISwapChain11.h @@ -0,0 +1,24 @@ +#ifndef ISwapChain11_h__ +#define ISwapChain11_h__ + +#include "../../ISwapChain.h" +#include +#include "render/dx11/ITexture2D11.h" +#include + +namespace LifeGraphics{ + class ISwapChain11 : public ISwapChain{ + public: + ISwapChain11(ID3D11Device* device, LifeCore::IRenderWindow* wnd); + LifeGraphics::ITexture2D* GetBackBuffer(uint8 id); + LifeGraphics::ITexture2D* GetDepthBuffer(uint8 id); + void Resize(uint16 width, uint16 height); + void Present(); + ~ISwapChain11(); + private: + IDXGISwapChain* swap_chain; + LifeGraphics::ITexture2D11* back_buffer; + LifeGraphics::ITexture2D11* depth_buffer; + }; +} +#endif // ISwapChain11_h__ diff --git a/LifeGraphics/render/dx11/ITexture2D11.h b/LifeGraphics/render/dx11/ITexture2D11.h new file mode 100644 index 0000000..e67acc4 --- /dev/null +++ b/LifeGraphics/render/dx11/ITexture2D11.h @@ -0,0 +1,325 @@ +#ifndef ITEXTURE2D11_H__ +#define ITEXTURE2D11_H__ + +#include "render/ITexture2D.h" +#include "render/dx11/IRenderTargetViewDX11.h" +#include "render/dx11/IShaderResourceView11.h" +#include "render/dx11/ISurface11.h" +#include "ResourceManager.h" +#include +#include +#include +#include +#include "Globals.h" +#include + +namespace LifeGraphics +{ + class ITexture2D11 : public ITexture2D + { + private: + int FormatToSize(DXGI_FORMAT format) + { + switch(format) + { + case DXGI_FORMAT_R32G32B32A32_TYPELESS: + case DXGI_FORMAT_R32G32B32A32_FLOAT: + case DXGI_FORMAT_R32G32B32A32_UINT: + case DXGI_FORMAT_R32G32B32A32_SINT: + return 16; + case DXGI_FORMAT_R32G32B32_TYPELESS: + case DXGI_FORMAT_R32G32B32_FLOAT: + case DXGI_FORMAT_R32G32B32_UINT: + case DXGI_FORMAT_R32G32B32_SINT: + return 12; + case DXGI_FORMAT_R16G16B16A16_TYPELESS: + case DXGI_FORMAT_R16G16B16A16_FLOAT: + case DXGI_FORMAT_R16G16B16A16_UNORM: + case DXGI_FORMAT_R16G16B16A16_UINT: + case DXGI_FORMAT_R16G16B16A16_SNORM: + case DXGI_FORMAT_R16G16B16A16_SINT: + case DXGI_FORMAT_R32G32_TYPELESS: + case DXGI_FORMAT_R32G32_FLOAT: + case DXGI_FORMAT_R32G32_UINT: + case DXGI_FORMAT_R32G32_SINT: + case DXGI_FORMAT_R32G8X24_TYPELESS: + case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: + case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: + case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: + return 8; + case DXGI_FORMAT_R10G10B10A2_TYPELESS: + case DXGI_FORMAT_R10G10B10A2_UNORM: + case DXGI_FORMAT_R10G10B10A2_UINT: + case DXGI_FORMAT_R11G11B10_FLOAT: + case DXGI_FORMAT_R8G8B8A8_TYPELESS: + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + case DXGI_FORMAT_R8G8B8A8_UINT: + case DXGI_FORMAT_R8G8B8A8_SNORM: + case DXGI_FORMAT_R8G8B8A8_SINT: + case DXGI_FORMAT_R16G16_TYPELESS: + case DXGI_FORMAT_R16G16_FLOAT: + case DXGI_FORMAT_R16G16_UNORM: + case DXGI_FORMAT_R16G16_UINT: + case DXGI_FORMAT_R16G16_SNORM: + case DXGI_FORMAT_R16G16_SINT: + case DXGI_FORMAT_R32_TYPELESS: + case DXGI_FORMAT_D32_FLOAT: + case DXGI_FORMAT_R32_FLOAT: + case DXGI_FORMAT_R32_UINT: + case DXGI_FORMAT_R32_SINT: + case DXGI_FORMAT_R24G8_TYPELESS: + case DXGI_FORMAT_D24_UNORM_S8_UINT: + case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: + case DXGI_FORMAT_X24_TYPELESS_G8_UINT: + case DXGI_FORMAT_R9G9B9E5_SHAREDEXP: + case DXGI_FORMAT_B8G8R8A8_UNORM: + case DXGI_FORMAT_B8G8R8X8_UNORM: + case DXGI_FORMAT_B8G8R8A8_TYPELESS: + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + case DXGI_FORMAT_B8G8R8X8_TYPELESS: + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: + return 4; + case DXGI_FORMAT_R8G8_TYPELESS: + case DXGI_FORMAT_R8G8_UNORM: + case DXGI_FORMAT_R8G8_UINT: + case DXGI_FORMAT_R8G8_SNORM: + case DXGI_FORMAT_R8G8_SINT: + case DXGI_FORMAT_R16_TYPELESS: + case DXGI_FORMAT_R16_FLOAT: + case DXGI_FORMAT_D16_UNORM: + case DXGI_FORMAT_R16_UNORM: + case DXGI_FORMAT_R16_UINT: + case DXGI_FORMAT_R16_SNORM: + case DXGI_FORMAT_R16_SINT: + case DXGI_FORMAT_B5G6R5_UNORM: + case DXGI_FORMAT_B5G5R5A1_UNORM: + return 2; + case DXGI_FORMAT_R8_TYPELESS: + case DXGI_FORMAT_R8_UNORM: + case DXGI_FORMAT_R8_UINT: + case DXGI_FORMAT_R8_SNORM: + case DXGI_FORMAT_R8_SINT: + case DXGI_FORMAT_A8_UNORM: + return 1; + } + return -1; + } + public: + ID3D11ShaderResourceView* cubeResourceView; + ISurface* getSurface(int id) + { + return &surfaces[id]; + } + bool IsVideoTexture() + { + return false; + } + ITexture2D11(ID3D11Device* _device) + { + IsRenderTarget = 0; + IsDepthTarget = 0; + pDevice = _device; + pDevice->GetImmediateContext(&context); + } + ~ITexture2D11() + { + for(UINT i = 0; i rtViews; + std::vector srViews; + std::vector dsViews; + + ID3D11Texture2D* texture; + ID3D11Device* pDevice; + ID3D11DeviceContext* context; + LifeCore::IDataRectangle rectangle; + D3D11_MAPPED_SUBRESOURCE mappedData; + int t_width, t_height; + int IsRenderTarget; + int IsDepthTarget; + + IShaderResourceView* getSRView() + { + return srViews[0]; + } + + IShaderResourceView* getSRView(int index) + { + return srViews[index]; + } + + ID3D11DepthStencilView* getDSView() + { + return dsViews[0]; + } + + ID3D11DepthStencilView* getDSView(int index) + { + return dsViews[index]; + } + + IRenderTargetView* getRTView() + { + return rtViews[0]; + } + + IRenderTargetView* getRTView(int index) + { + return rtViews[index]; + } + + void addRenderTargetView(IRenderTargetView11* view) + { + rtViews.push_back(view); + surfaces[rtViews.size() - 1].setRTV(view->view); + } + + void addDepthStencilView(ID3D11DepthStencilView* view) + { + dsViews.push_back(view); + surfaces[dsViews.size() - 1].setDSV(view); + } + + void addShaderResourceView(IShaderResourceView11* view) + { + srViews.push_back(view); + surfaces[srViews.size() - 1].setSRV(view->view); + } + + void FromTexture(ID3D11Texture2D* tex) + { + if (tex) + { + texture = tex; + + D3D11_TEXTURE2D_DESC desc; + texture->GetDesc(&desc); + + t_width = desc.Width; + t_height = desc.Height; + + bpp = FormatToSize(desc.Format); + + this->Description.Width = desc.Width; + this->Description.Height = desc.Height; + this->Description.SampleCount = desc.SampleDesc.Count; + this->Description.SampleQuality = desc.SampleDesc.Quality; + this->Description.Format = desc.Format; + } + } + ID3D11Texture2D* GetData() + { + return texture; + } + + LifeCore::IDataRectangle* Map(int level, int read, int write) + { + D3D11_MAP flags = D3D11_MAP_WRITE_DISCARD; + + if(!read && !write) + throw "ff"; + + if(read && !write) + flags = D3D11_MAP_READ; + else if(!read && write) + flags = D3D11_MAP_WRITE_DISCARD; + else if (read && write) + flags = D3D11_MAP_READ_WRITE; + + context->Map(texture, level, flags,0, &mappedData); + + rectangle.pitch = mappedData.RowPitch; + rectangle.data.Reset((char*)mappedData.pData, t_width * t_height * bpp); + + return &rectangle; + + // return new LifeCore::IDataRectangle(stm.RowPitch, stm.Data.DataPointer); + } + void Unmap(int level) + { + context->Unmap(texture, level); + // g_textureData.Device.ImmediateContext.UnmapSubresource(g_textureData, 0); + + //g_textureData.AsSurface().Unmap(); + } + + int Width() + { + return t_width; + } + + int Height() + { + return t_height; + } + + void ToFile(const String& path) + { + D3DX11SaveTextureToFile(context,texture, D3DX11_IFF_DDS, path.c_str()); + LifeCore::CLog::me()->Write(AS_STRING("OH SHIIIT!!! Texture saving!")); + //Logger.Instance.Write("WARNING! Saving render target to file!" + "\n" + path); + //Texture2D.ToFile((this.g_parent as RendererDX11).GetDeviceContext(), g_textureData,ImageFileFormat.Jpg, path ); + } + void CompleteAsyncRequest(IResource* source) + { + //Core::CLog::me()->Write(String(_text("CompleteAsyncRequest called in texture2D!")) + source->Name); + //Texture2D11 src = (Texture2D11)source; + // this.FromTexture(src.g_textureData); + } + + void Load(LifeCore::IDataStreamPtr source) + { + ID3D11Resource* tex = 0; + char* dataBuffer = new char[source->Length()]; + source->Read(dataBuffer, source->Length()); + HRESULT result = 0; + D3DX11_IMAGE_LOAD_INFO info; + + info.Width = D3DX11_DEFAULT; + info.Height = D3DX11_DEFAULT; + info.Depth = D3DX11_DEFAULT; + info.FirstMipLevel = 0; + info.MipLevels = 6; + info.Usage = (D3D11_USAGE) D3DX11_DEFAULT; + info.BindFlags = D3DX11_DEFAULT; + info.CpuAccessFlags = D3DX11_DEFAULT; + info.MiscFlags = D3DX11_DEFAULT; + info.Format = DXGI_FORMAT_FROM_FILE; + info.Filter = D3DX11_FILTER_TRIANGLE; + info.MipFilter = D3DX11_FILTER_TRIANGLE; + info.pSrcInfo = NULL; + + result = D3DX11CreateTextureFromMemory(pDevice, dataBuffer,source->Length(),&info, 0, &tex, 0); + delete[] dataBuffer; + if(result == S_OK) + FromTexture((ID3D11Texture2D*) tex); + else + LifeCore::CLog::me()->Write(String(_text("Can not load texture!"))); + } + + private: + ISurface11 surfaces[6]; + /* virtual int Width(); + virtual int Height(); + virtual LifeCore::IDataRectangle* Map(int level, bool read, bool write, int bpp) = 0; + virtual void Unmap(int level) = 0; + virtual void ToFile(const String& path) = 0; + virtual void Load(const Core::IDataStream* src) {} + virtual void CompleteAsyncRequest(IResource* source) {}*/ + }; +} + +#endif \ No newline at end of file diff --git a/LifeGraphics/render/dx11/ITextureSampler11.h b/LifeGraphics/render/dx11/ITextureSampler11.h new file mode 100644 index 0000000..65d3cf5 --- /dev/null +++ b/LifeGraphics/render/dx11/ITextureSampler11.h @@ -0,0 +1,36 @@ +#ifndef ITextureSampler11_h__ +#define ITextureSampler11_h__ +#include "render/ITextureSampler.h" + +#include +#include +#include +#include "Globals.h" + +namespace LifeGraphics +{ + class ITextureSampler11 : public ITextureSampler + { + public: + ID3D11SamplerState* state; + + ITextureSampler11(ISamplerDescription* desc, ID3D11Device* dev) + { + D3D11_SAMPLER_DESC d; + ZeroMemory(&d, sizeof(D3D11_SAMPLER_DESC)); + d.AddressU = static_cast(desc->AddressU); + d.AddressV = static_cast(desc->AddressV); + d.AddressW = static_cast(desc->AddressW); + d.MaxAnisotropy = 1; + d.Filter = static_cast(desc->Filter); + d.ComparisonFunc = static_cast(desc->CmpFunc); + d.MaxLOD = 3.402823466e+38F; + dev->CreateSamplerState(&d, &state); + } + ~ITextureSampler11() + { + SAFE_RELEASE(state); + } + }; +} +#endif // ITextureSampler11_h__ \ No newline at end of file diff --git a/LifeGraphics/render/dx11/IVertexBuffer11.h b/LifeGraphics/render/dx11/IVertexBuffer11.h new file mode 100644 index 0000000..e163e13 --- /dev/null +++ b/LifeGraphics/render/dx11/IVertexBuffer11.h @@ -0,0 +1,109 @@ +#ifndef I_VERTEX_BUFFER11_H__ +#define I_VERTEX_BUFFER11_H__ + +#include "render/IVertexBuffer.h" + +#include "ResourceManager.h" +#include "IMemoryStream.h" + +#include +#include +#include +#include "Globals.h" + +namespace LifeGraphics +{ + class IVertexBuffer11 : public IVertexBuffer + { + public: + ID3D11Buffer* vb; + ID3D11Device* device; + ID3D11DeviceContext* context; + D3D11_MAPPED_SUBRESOURCE mappedData; + LifeCore::IMemoryStream stm; + + ~IVertexBuffer11() + { + SAFE_RELEASE(vb); + } + IVertexBuffer11(ID3D11Device* _device, void* pBuffer, int _size, int _stride, int staging) + { + device = _device; + length = _size; + stride = _stride; + + //g_desc = new BufferDescription(size, D3D11.ResourceUsage.Default, + // BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); + D3D11_BUFFER_DESC bd; + ZeroMemory( &bd, sizeof(bd) ); + if(pBuffer) + bd.Usage = D3D11_USAGE_DEFAULT; + else + bd.Usage = D3D11_USAGE_DYNAMIC; + if(staging) + bd.Usage = D3D11_USAGE_STAGING; + + bd.ByteWidth = _size; + bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; + + if(bd.Usage == D3D11_USAGE_DEFAULT) + bd.CPUAccessFlags = 0; + else if(bd.Usage == D3D11_USAGE_DYNAMIC) + bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + else if(bd.Usage == D3D11_USAGE_STAGING) + bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE | D3D11_CPU_ACCESS_READ; + + if(pBuffer) + { + D3D11_SUBRESOURCE_DATA InitData; + ZeroMemory( &InitData, sizeof(InitData) ); + InitData.pSysMem = pBuffer; + + device->CreateBuffer( &bd, &InitData, &vb ); + } + else + device->CreateBuffer( &bd, NULL, &vb ); + device->GetImmediateContext(&context); + //if( FAILED( hr ) ) + // return hr; + } + LifeCore::IDataStream* Map(int offset, int flags) + { + D3D11_MAP mapFlags; + + if (flags & LF_Discard) + mapFlags = D3D11_MAP_WRITE_DISCARD; + else if ((flags & LF_NoOverwrite)) + mapFlags = D3D11_MAP_WRITE_NO_OVERWRITE; + + else if ((flags & LF_ReadOnly)) + mapFlags = D3D11_MAP_READ; + + else if (flags == LF_None) + mapFlags = D3D11_MAP_READ_WRITE; + else + mapFlags = D3D11_MAP_WRITE; + + context->Map(vb,0, mapFlags,0, &mappedData); + stm.Reset(((char*)mappedData.pData) + offset, length); + return &stm; + } + void Update(void* data, size_t length) + { + D3D11_BOX b; + b.left = 0; + b.right = 1; + b.top = 0; + b.bottom = 1; + b.front = 0; + b.back = 0; + context->UpdateSubresource(vb, 0, NULL, data, length, 0); + } + void Unmap() + { + context->Unmap(vb, 0); + } + }; +} + +#endif \ No newline at end of file diff --git a/LifeGraphics/render/dx11/IVertexShader11.h b/LifeGraphics/render/dx11/IVertexShader11.h new file mode 100644 index 0000000..de991bc --- /dev/null +++ b/LifeGraphics/render/dx11/IVertexShader11.h @@ -0,0 +1,93 @@ +#ifndef IVertexShader11_h__ +#define IVertexShader11_h__ + + +#include +#include +#include +#include "render/IShader.h" +#include "render/IMacroDefine.h" +#include +#include "Globals.h" +namespace LifeGraphics +{ + class IVertexShader11 : public IShader + { + public: + ID3D11VertexShader* g_shader; + ID3DBlob* g_bytecode; + ID3D11Device* g_device; + IVertexShader11(ID3D11Device* device) + { + g_device = device; + } + ~IVertexShader11() + { + SAFE_RELEASE(g_bytecode); + SAFE_RELEASE(g_shader); + } + void* Disassemble() + { + return 0; + } + SHADER_TYPE getShaderType() + { + return ST_VS; + } + char* GetByteCode() + { + return (char*)0; + } + void Compile(LifeCore::IDataStreamPtr stream, std::vector* defines) + { + char* shader = new char[stream->Length()]; + stream->Read(shader, stream->Length()); + Compile(shader, stream->Length(), defines); + delete[]shader; + } + void Compile(const char* pData, int length, std::vector* defines) + { + HRESULT hr; + g_bytecode = NULL; + ID3D10Blob* errorBlob; + + if(!defines) + { + hr = D3DX11CompileFromMemory(pData, length, "system_shader", NULL, NULL, "main_VS", "vs_5_0",0,0,NULL, &g_bytecode,&errorBlob, NULL); + } + else + { + //create macro + D3D10_SHADER_MACRO* macros = new D3D10_SHADER_MACRO[defines->size() + 1]; + int cnt = defines->size(); + for(int i = 0; i < cnt; i++) + { + macros[i].Definition = defines[0][i].definition.c_str(); + macros[i].Name = defines[0][i].name.c_str(); + } + D3D10_SHADER_MACRO m; + m.Definition = NULL; + m.Name = NULL; + macros[cnt] = m; + + hr = D3DX11CompileFromMemory(pData, length, "system_shader", macros, NULL, "main_VS", "vs_5_0",0,0,NULL, &g_bytecode,&errorBlob, NULL); + delete[] macros; + } + if( FAILED( hr ) ) + { + printf("SHADER COMPILE ERROR: %s", errorBlob->GetBufferPointer()); + } + hr = g_device->CreateVertexShader(g_bytecode->GetBufferPointer(), g_bytecode->GetBufferSize(), NULL, &g_shader); + } + void Load(LifeCore::IDataStreamPtr src) + { + printf("Load VS is not implemented: %i\n", src->Length()); + } + void CompleteAsyncRequest(IResource* source) + { + //printf("VS async not implemented: %s\n", source->Name.c_str()); + } + }; +} + +#endif // IPixelShader11_h__ \ No newline at end of file diff --git a/LifeGraphics/render/dx11/RendererDX11.cpp b/LifeGraphics/render/dx11/RendererDX11.cpp new file mode 100644 index 0000000..fea0c0b --- /dev/null +++ b/LifeGraphics/render/dx11/RendererDX11.cpp @@ -0,0 +1,1335 @@ +#pragma once +#include "render/dx11/RendererDX11.h" + +#include "vfs/fs_win/IFSContentProvider.h" +#include "LString.h" +#include "vfs/IFile.h" +#include +#include "render/dx11/IConstantBuffer11.h" +#include "render/IMaterial.h" +#include "D3DX10math.h" +#include "render/dx11/IDepthStencilState11.h" +#include +#include "d3d11sdklayers.h" +#include +#include + +namespace LifeGraphics +{ + HRESULT RendererDX11::Open(RenderParams params) + { + HRESULT hr = S_OK; + g_params = params; + + D3D_DRIVER_TYPE drivers[]= + { + D3D_DRIVER_TYPE_HARDWARE, + D3D_DRIVER_TYPE_WARP, + D3D_DRIVER_TYPE_REFERENCE + }; + + D3D_FEATURE_LEVEL featureLevels[] = + { + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, + }; + + int numDrivers = 1; + + for(int i = 0; i< numDrivers; i++) + { + g_driverType = drivers[i]; +#ifdef _DEBUG + hr = D3D11CreateDevice(0,g_driverType,NULL, D3D11_CREATE_DEVICE_DEBUG,featureLevels,3,D3D11_SDK_VERSION,&g_pDevice, &g_pFeatureLevel, &g_pDeviceContext); +#else + hr = D3D11CreateDevice(0,g_driverType,NULL, NULL,featureLevels,3,D3D11_SDK_VERSION,&g_pDevice, &g_pFeatureLevel, &g_pDeviceContext); +#endif + if(SUCCEEDED(hr)) + break; + } + + + + + char ps[] = "Texture2D diffuseTexture : register(t0);" \ + "SamplerState diffuseSampler : register(s0);" \ + "half4 main_PS( in float4 Pos : SV_Position, in float2 t : TEXCOORD0 ) : SV_Target" \ + "{half4 data = diffuseTexture.Sample(diffuseSampler, t); return data; }"; + + char vs[] = "struct VS_OUTPUT" \ + "{float4 Position : SV_Position;" \ + "float2 Texcoord : TEXCOORD0;};" \ + "void main_VS( in float4 Pos : POSITION, in float2 t : TEXCOORD0, out float4 Position : SV_Position, out float2 texcoord : TEXCOORD0 )" \ + "{Position = Pos; texcoord = t;}"; + + char vbPs[] = "cbuffer data : register(b0){float4 vBlurOffsets[9];};" \ + "Texture2D originalSceneSampler : register(t0);" \ + "SamplerState linearSampler : register(s0);" \ + "float4 main_PS( in float4 Position : SV_Position, in float2 texcoord : TEXCOORD0 ) : SV_Target" \ + "{ float4 color = { 0.0f, 0.0f, 0.0f, 0.0f };" \ + "for( int i = 0; i < 9; i++ )" \ + " color += originalSceneSampler.Sample( linearSampler, texcoord + float2( 0.0f, vBlurOffsets[i].x ) ) * vBlurOffsets[i].y;" \ + "return float4( color.rgb, 1.0f ); }"; + + ID3DBlob* vbblob = NULL; + hr = D3DX11CompileFromMemory(vbPs, strlen(vbPs), "system_shader", NULL, NULL, "main_PS", "ps_4_0",0,0,NULL, &vbblob,NULL, NULL); + if( FAILED( hr ) ) + return hr; + hr = g_pDevice->CreatePixelShader(vbblob->GetBufferPointer(), vbblob->GetBufferSize(), NULL, &psVerticalBlur); + if( FAILED( hr ) ) + return hr; + SAFE_RELEASE(vbblob); + + ID3DBlob* blob = NULL; + hr = D3DX11CompileFromMemory(ps, strlen(ps), "system_shader", NULL, NULL, "main_PS", "ps_4_0",0,0,NULL, &blob,NULL, NULL); + if( FAILED( hr ) ) + return hr; + hr = g_pDevice->CreatePixelShader(blob->GetBufferPointer(), blob->GetBufferSize(), NULL, &psQuad); + if( FAILED( hr ) ) + return hr; + + ID3DBlob* vsblob = NULL; + hr = D3DX11CompileFromMemory(vs, strlen(vs), "system_shader2", NULL, NULL, "main_VS", "vs_4_0",0,0,NULL, &vsblob,NULL, NULL); + if( FAILED( hr ) ) + return hr; + hr = g_pDevice->CreateVertexShader(vsblob->GetBufferPointer(), vsblob->GetBufferSize(), NULL, &vsQuad); + if( FAILED( hr ) ) + return hr; + + SimpleVertex geometry[] = + { + LifeMath::float4(-1.0f,-1.0f,0.0f,1.0f), LifeMath::float2(0.0f,1.0f), + LifeMath::float4(-1.0f,1.0f,0.0f,1.0f), LifeMath::float2(0.0f,0.0f), + + LifeMath::float4(1.0f,-1.0f,0.0f,1.0f), LifeMath::float2(1.0f,1.0f), + LifeMath::float4(1.0f,1.0f,0.0f,1.0f), LifeMath::float2(1.0f,0.0f), + }; + + char ds2x2_data[] = + " cbuffer texInfo : register(b12)" \ + "{" \ + " float2 texelSize;" \ + " float2 dummy;" \ + "};" \ + "Texture2D diffuseMap : register(t0);" \ + "SamplerState pointSampler : register(s0);" \ + "static const float PixelKernel[13] =" \ + "{" \ + " -6.0f," \ + " -5.0f," \ + " -4.0f," \ + " -3.0f," \ + " -2.0f," \ + " -1.0f," \ + " 0.0f," \ + " 1.0f," \ + " 2.0f," \ + " 3.0f," \ + " 4.0f," \ + " 5.0f," \ + " 6.0f," \ + "};" \ + "static const float BlurWeights[13] = " \ + "{" \ + " 0.001108f," \ + " 0.004382f," \ + " 0.013497f," \ + " 0.032379f," \ + " 0.060493f," \ + " 0.088017f," \ + " 0.099735f," \ + " 0.088017f," \ + " 0.060493f," \ + " 0.032379f," \ + " 0.013497f," \ + " 0.004382f," \ + " 0.001108f," \ + "};" \ + "float4 main_PS(in float4 Position : SV_Position, in float2 texcoord : TEXCOORD0) : SV_Target" \ + "{" \ + " float4 color = (float4)0;" \ + "[Unroll]" \ + " for(int i = 0; i < 13; i++)" \ + " {" \ + " color += diffuseMap.Sample(pointSampler, texcoord + float2(0.0f, PixelKernel[i] * texelSize.y)) * BlurWeights[i];" \ + " color += diffuseMap.Sample(pointSampler, texcoord + float2(PixelKernel[i] * texelSize.x, 0.0f)) * BlurWeights[i];" \ + " }" \ + " return color;" \ + "} "; + + ds2x2 = CompileUserShaderPS(ds2x2_data); + ds2x2cb = CreateConstantBuffer(sizeof(LifeMath::float4)); + + D3D11_BUFFER_DESC bd; + ZeroMemory( &bd, sizeof(bd) ); + bd.Usage = D3D11_USAGE_DEFAULT; + bd.ByteWidth = sizeof( SimpleVertex ) * 4; + bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; + bd.CPUAccessFlags = 0; + D3D11_SUBRESOURCE_DATA InitData; + ZeroMemory( &InitData, sizeof(InitData) ); + InitData.pSysMem = geometry; + + hr = g_pDevice->CreateBuffer( &bd, &InitData, &fsQuadBuffer ); + if( FAILED( hr ) ) + return hr; + + D3D11_INPUT_ELEMENT_DESC desc[] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 16, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + }; + + UINT numElements = ARRAYSIZE( desc ); + fsQuadLayout = 0; + hr = g_pDevice->CreateInputLayout(desc,numElements, vsblob->GetBufferPointer(), vsblob->GetBufferSize(), &fsQuadLayout); + if( FAILED( hr ) ) + return hr; + SAFE_RELEASE(vsblob); + SAFE_RELEASE(blob); + + /*D3D11_VIEWPORT vp; + vp.Height = (float)g_params.ScreenHeight; + vp.Width = (float)g_params.ScreenWidth; + vp.TopLeftX = 0.0f; + vp.TopLeftY = 0.0f; + vp.MinDepth = 0.0f; + vp.MaxDepth = 1.0f; + g_pDeviceContext->RSSetViewports(1, &vp);*/ + + D3D11_SAMPLER_DESC lsDesc; + ZeroMemory(&lsDesc, sizeof(D3D11_SAMPLER_DESC)); + lsDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; + lsDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; + lsDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; + lsDesc.ComparisonFunc = D3D11_COMPARISON_NEVER; + lsDesc.MaxAnisotropy = 0; + lsDesc.MinLOD = 0; + lsDesc.MaxLOD = 16; + lsDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; + + g_pDevice->CreateSamplerState(&lsDesc,&samplerPoint); + + g_pDeviceContext->PSSetSamplers(1,1, &samplerPoint); + + D3D11_SAMPLER_DESC lsDesc2; + ZeroMemory(&lsDesc2, sizeof(D3D11_SAMPLER_DESC)); + lsDesc2.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; + lsDesc2.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; + lsDesc2.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; + lsDesc2.ComparisonFunc = D3D11_COMPARISON_NEVER; + lsDesc2.MaxAnisotropy = 16; + lsDesc2.MinLOD = 0; + lsDesc2.MaxLOD = 16; + lsDesc2.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + + g_pDevice->CreateSamplerState(&lsDesc2,&samplerLinear); + g_pDeviceContext->PSSetSamplers(0,1, &samplerLinear); + + + D3D11_RASTERIZER_DESC rsDesc; + rsDesc.AntialiasedLineEnable = false; + rsDesc.CullMode = D3D11_CULL_BACK; + rsDesc.DepthBias = 0; + rsDesc.DepthBiasClamp = 0.0f; + rsDesc.SlopeScaledDepthBias = 0.0f; + rsDesc.DepthClipEnable = TRUE; + rsDesc.FillMode = D3D11_FILL_SOLID; + rsDesc.FrontCounterClockwise = FALSE; + rsDesc.MultisampleEnable = FALSE; + rsDesc.ScissorEnable = TRUE; + + g_pDevice->CreateRasterizerState(&rsDesc, &rState); + g_pDeviceContext->RSSetState(rState); + + vbBuffer = CreateConstantBuffer(16 * 16); + + return hr; + } + + ISwapChain* RendererDX11::CreateSwapChain(LifeCore::IRenderWindow* window){ + return static_cast(new ISwapChain11(g_pDevice, window)); + } + + int RendererDX11::GetDisplayModes(LifeCore::DisplayMode** modeList) + { + //get display mode count + UINT numModes; + pDXGIOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &numModes, NULL); + + //get display modes + DXGI_MODE_DESC* modes = new DXGI_MODE_DESC[numModes]; + pDXGIOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &numModes, modes); + + LifeCore::DisplayMode* m = new LifeCore::DisplayMode[numModes]; + int pos = 0; + for(UINT i = 0; i < numModes; i++) + { + if(pos==0) + { + m[pos].width = modes[i].Width; + m[pos].height = modes[i].Height; + m[pos].refreshRate = modes[i].RefreshRate.Numerator; + m[pos].bpp = 4; + pos++; + } + else + { + int prev = i - 1; + if(modes[prev].Width != modes[i].Width || modes[prev].Height != modes[i].Height || modes[prev].RefreshRate.Numerator != modes[i].RefreshRate.Numerator) + { + m[pos].width = modes[i].Width; + m[pos].height = modes[i].Height; + m[pos].refreshRate = modes[i].RefreshRate.Numerator; + m[pos].bpp = 4; + pos++; + } + } + } + + (*modeList) = m; + + return pos; + } + + void RendererDX11::DownSample2x2(ITexture2D* src, ITexture2D* dst, ITexture2D* depth) + { + LifeMath::float4 data; + + data.X = 1.0f / (float)dst->Width(); + data.Y = 1.0f / (float)dst->Height(); + + ds2x2cb->Update(&data, sizeof(LifeMath::float4)); + + LifeMath::Rect2i vp = LifeMath::Rect2i(0,0,dst->Width(), dst->Height()); + SetPixelShader(ds2x2); + SetPSConstantBuffer(ds2x2cb, 12); + SetScissorRectangle(vp); + SetViewport(vp); + SetPSTexture(src, 0); + SetTargets(dst, depth); + RenderToTextureNoPs(); + } + + void RendererDX11::BlurGaussH(ITexture2D* src, ITexture2D* dst, ITexture2D* depth) + { + LifeMath::float4 data; + + data.X = 1.0f / (float)dst->Width(); + data.Y = 1.0f / (float)dst->Height(); + + ds2x2cb->Update(&data, sizeof(LifeMath::float4)); + } + void RendererDX11::BlurGaussV(ITexture2D* src, ITexture2D* dst, ITexture2D* depth) + { + LifeMath::float4 data; + + data.X = 1.0f / (float)src->Width(); + data.Y = 1.0f / (float)src->Height(); + + ds2x2cb->Update(&data, sizeof(LifeMath::float4)); + } + + void ClearD3D11DeviceContext( ID3D11DeviceContext* pd3dDeviceContext ) + { + // Unbind all objects from the immediate context + if (pd3dDeviceContext == NULL) return; + + ID3D11ShaderResourceView* pSRVs[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; + ID3D11RenderTargetView* pRTVs[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; + ID3D11DepthStencilView* pDSV = NULL; + ID3D11Buffer* pBuffers[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; + ID3D11SamplerState* pSamplers[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; + UINT StrideOffset[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; + + // Shaders + pd3dDeviceContext->VSSetShader( NULL, NULL, 0 ); + pd3dDeviceContext->HSSetShader( NULL, NULL, 0 ); + pd3dDeviceContext->DSSetShader( NULL, NULL, 0 ); + pd3dDeviceContext->GSSetShader( NULL, NULL, 0 ); + pd3dDeviceContext->PSSetShader( NULL, NULL, 0 ); + + // IA clear + pd3dDeviceContext->IASetVertexBuffers( 0, 16, pBuffers, StrideOffset, StrideOffset ); + pd3dDeviceContext->IASetIndexBuffer( NULL, DXGI_FORMAT_R16_UINT, 0 ); + pd3dDeviceContext->IASetInputLayout( NULL ); + + // Constant buffers + pd3dDeviceContext->VSSetConstantBuffers( 0, 14, pBuffers ); + pd3dDeviceContext->HSSetConstantBuffers( 0, 14, pBuffers ); + pd3dDeviceContext->DSSetConstantBuffers( 0, 14, pBuffers ); + pd3dDeviceContext->GSSetConstantBuffers( 0, 14, pBuffers ); + pd3dDeviceContext->PSSetConstantBuffers( 0, 14, pBuffers ); + + // Resources + pd3dDeviceContext->VSSetShaderResources( 0, 16, pSRVs ); + pd3dDeviceContext->HSSetShaderResources( 0, 16, pSRVs ); + pd3dDeviceContext->DSSetShaderResources( 0, 16, pSRVs ); + pd3dDeviceContext->GSSetShaderResources( 0, 16, pSRVs ); + pd3dDeviceContext->PSSetShaderResources( 0, 16, pSRVs ); + + // Samplers + pd3dDeviceContext->VSSetSamplers( 0, 16, pSamplers ); + pd3dDeviceContext->HSSetSamplers( 0, 16, pSamplers ); + pd3dDeviceContext->DSSetSamplers( 0, 16, pSamplers ); + pd3dDeviceContext->GSSetSamplers( 0, 16, pSamplers ); + pd3dDeviceContext->PSSetSamplers( 0, 16, pSamplers ); + + // Render targets + pd3dDeviceContext->OMSetRenderTargets( 8, pRTVs, pDSV ); + + // States + FLOAT blendFactor[4] = { 0,0,0,0 }; + pd3dDeviceContext->OMSetBlendState( NULL, blendFactor, 0xFFFFFFFF ); + pd3dDeviceContext->OMSetDepthStencilState( NULL, 0 ); + pd3dDeviceContext->RSSetState( NULL ); + } + + void RendererDX11::BeginCleanup() + { + g_pDeviceContext->ClearState(); + g_pDeviceContext->Flush(); + ClearD3D11DeviceContext(g_pDeviceContext); + SAFE_DELETE(vbBuffer); + + SAFE_RELEASE(rState); + SAFE_RELEASE(samplerLinear); + SAFE_RELEASE(samplerPoint); + SAFE_RELEASE(fsQuadLayout); + SAFE_RELEASE(fsQuadBuffer); + SAFE_RELEASE(psVerticalBlur); + SAFE_RELEASE(psQuad); + SAFE_RELEASE(vsQuad); + + g_pDeviceContext->ClearState(); + g_pDeviceContext->Flush(); + } + + void RendererDX11::EndCleanup() + { + g_pDeviceContext->ClearState(); + g_pDeviceContext->Flush(); + + SAFE_RELEASE(g_pDevice); + g_pDeviceContext->Release(); + g_pDeviceContext->ClearState(); + g_pDeviceContext->Flush(); + SAFE_RELEASE(g_pDeviceContext); + } + + void RendererDX11::Close() + { + /*#ifdef _DEBUG + ID3D11Debug* pDebug; + g_pDevice->QueryInterface( IID_ID3D11Debug, (VOID**)(&pDebug) ); + pDebug->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); + pDebug->Release(); + #endif*/ + } + + IRasterizerState* RendererDX11::CreateRasterizerState(FILL_MODE mode) + { + return new IRasterizerState11(mode, g_pDevice); + } + + IRasterizerState* RendererDX11::CreateRasterizerState(FILL_MODE mode, CULL_MODE cmode) + { + return new IRasterizerState11(mode, g_pDevice, cmode); + } + void RendererDX11::SetRasterizerState(IRasterizerState* state) + { + IRasterizerState11* s = static_cast(state); + g_pDeviceContext->RSSetState(s->state); + } + + IShader* RendererDX11::CompileUserShaderPS(char* data) + { + //HRESULT hr; + ID3D11PixelShader* ps; + + ID3DBlob* vbblob = NULL; + D3DX11CompileFromMemory(data, strlen(data), "system_shader", NULL, NULL, "main_PS", "ps_4_0",0,0,NULL, &vbblob,NULL, NULL); + + g_pDevice->CreatePixelShader(vbblob->GetBufferPointer(), vbblob->GetBufferSize(), NULL, &ps); + + ID3DBlob* dasm = NULL; + D3DDisassemble(vbblob->GetBufferPointer(), vbblob->GetBufferSize(), D3D_DISASM_ENABLE_DEFAULT_VALUE_PRINTS | D3D_DISASM_ENABLE_INSTRUCTION_NUMBERING, "OLOLO", &dasm); + + LifeCore::CLog::me()->Write(String((char*)dasm->GetBufferPointer())); + + IPixelShader11* nps = new IPixelShader11(g_pDevice); + nps->g_shader = ps; + vbblob->Release(); + return nps; + } + + void RendererDX11::SetPSTextureSampler(ITextureSampler* sampler, int slot) + { + ITextureSampler11* ts = static_cast(sampler); + g_pDeviceContext->PSSetSamplers(slot,1,&ts->state); + } + + void RendererDX11::SetPSTextureNULL(int slot) + { + ID3D11ShaderResourceView *const pSRV[1] = {NULL}; + + g_pDeviceContext->PSSetShaderResources(slot,1, pSRV); + } + + /*void RendererDX11::ClearBuffers(int flags, int rtColor, float zValue, int stencilValue) + { + if(g_pDevice) + { + float r,g,b,a; + r = (float)((rtColor >> 24) & 0xff) / 255.0f; + g = (float)((rtColor >> 16) & 0xff) / 255.0f; + b = (float)((rtColor >> 8) & 0xff) / 255.0f; + a = (float)((rtColor >> 0) & 0xff) / 255.0f; + + float clearColor[4]; + clearColor[0] = r; + clearColor[1] = g; + clearColor[2] = b; + clearColor[3] = a; + + if((flags & CF_COLOR_TARGET)!=0) + g_pDeviceContext->ClearRenderTargetView(currentBackBuffer, clearColor); + if((flags & CF_DEPTH_BUFFER)!=0) + g_pDeviceContext->ClearDepthStencilView(currentDepthBuffer,D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, zValue, (UINT8)stencilValue); + } + }*/ + void RendererDX11::ClearDepthStencil(ITexture2D* tex, float zValue, int stencilValue) + { + g_pDeviceContext->ClearDepthStencilView(static_cast(tex)->getDSView(),D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL,zValue, (UINT8)stencilValue); + } + void RendererDX11::ClearDepthStencil(ITexture2D* tex, int flag, float zValue, int stencilValue) + { + g_pDeviceContext->ClearDepthStencilView(static_cast(tex)->getDSView(),flag,zValue, (UINT8)stencilValue); + } + + void RendererDX11::ClearRenderTarget(ITexture2D* tex, LifeMath::float4* color) + { + float clearColor[4]; + clearColor[0] = color->X; + clearColor[1] = color->Y; + clearColor[2] = color->Z; + clearColor[3] = color->W; + IRenderTargetView11* rtv = static_cast(tex->getRTView()); + + g_pDeviceContext->ClearRenderTargetView(rtv->view, clearColor); + } + + void RendererDX11::ClearRenderTarget(ISurface* target, LifeMath::float4* color) + { + float clearColor[4]; + clearColor[0] = color->X; + clearColor[1] = color->Y; + clearColor[2] = color->Z; + clearColor[3] = color->W; + ISurface11* rtv = static_cast(target); + + g_pDeviceContext->ClearRenderTargetView(rtv->getRTV(), clearColor); + } + void RendererDX11::ClearDepthStencil(ISurface* target, int flag, float zValue, int stencilValue) + { + g_pDeviceContext->ClearDepthStencilView(static_cast(target)->getDSV(), flag, zValue, (UINT8)stencilValue); + } + ITexture2D* RendererDX11::CreateRenderTarget(int width, int height, int format) + { + DXGI_SAMPLE_DESC samDesc; + samDesc.Count = 1; + samDesc.Quality = 0; + + D3D11_TEXTURE2D_DESC desc = {0}; + desc.ArraySize = 1; + desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = 0; + desc.Format = (DXGI_FORMAT)format; + desc.MipLevels = 1; + desc.SampleDesc = samDesc; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.Width = width; + desc.Height = height; + + ID3D11Texture2D* tex; + g_pDevice->CreateTexture2D(&desc, NULL, &tex); + + ITexture2D11* rTex = new ITexture2D11(g_pDevice); + rTex->pDevice = g_pDevice; + rTex->FromTexture(tex); + + ID3D11RenderTargetView* rt; + g_pDevice->CreateRenderTargetView(tex, NULL, &rt); + + IRenderTargetView11* rtv = new IRenderTargetView11(rt); + + rTex->pDevice = g_pDevice; + rTex->IsRenderTarget = true; + rTex->addRenderTargetView(rtv); + + ID3D11ShaderResourceView* rv; + g_pDevice->CreateShaderResourceView(tex, NULL, &rv); + rTex->addShaderResourceView(new IShaderResourceView11(rv)); + + return rTex; + } + + ITexture2D* RendererDX11::LoadTexture2D(LifeCore::IDataStreamPtr src) + { + ITexture2D11* ret = new ITexture2D11(g_pDevice); + ret->pDevice = g_pDevice; + ret->Load(src); + ret->GetData()->AddRef(); + ID3D11ShaderResourceView* rv; + + g_pDevice->CreateShaderResourceView(ret->GetData(), NULL, &rv); + + ret->addShaderResourceView(new IShaderResourceView11(rv)); + + rv->AddRef(); + return ret; + } + ITexture2D* RendererDX11::CreateTexture2D(int canRead, int canWrite, int width, int height, int format) + { + DXGI_SAMPLE_DESC samDesc; + samDesc.Count = 1; + samDesc.Quality = 0; + + D3D11_TEXTURE2D_DESC desc = {0}; + desc.ArraySize = 1; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + + if (canWrite) + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + else + desc.CPUAccessFlags = 0; + if (canRead) + desc.CPUAccessFlags |= D3D11_CPU_ACCESS_READ; + + desc.Format = (DXGI_FORMAT)format; + desc.MipLevels = 1; + desc.SampleDesc = samDesc; + desc.Width = width; + desc.Height = height; + + if (canWrite | canRead) + desc.Usage = D3D11_USAGE_DYNAMIC; + // else + // desc.Usage = D3D11_USAGE_DYNAMIC; + + ID3D11Texture2D* tex; + g_pDevice->CreateTexture2D(&desc, NULL, &tex); + + ITexture2D11* result = new ITexture2D11(g_pDevice); + result->FromTexture(tex); + + ID3D11ShaderResourceView* rv; + g_pDevice->CreateShaderResourceView(tex, NULL, &rv); + + result->addShaderResourceView(new IShaderResourceView11(rv)); + + rv->AddRef(); + + tex->AddRef(); + return result; + } + + void RendererDX11::RenderToTextureNoShader() + { + g_pDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + g_pDeviceContext->IASetInputLayout(fsQuadLayout); + + UINT stride = sizeof(SimpleVertex); + UINT offset = 0; + + g_pDeviceContext->IASetVertexBuffers(0,1, &fsQuadBuffer, &stride, &offset); + g_pDeviceContext->Draw(4, 0); + } + + void RendererDX11::RenderToTexture() + { + g_pDeviceContext->PSSetShader(psQuad, NULL, 0); + g_pDeviceContext->VSSetShader(vsQuad, NULL, 0); + + g_pDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + g_pDeviceContext->IASetInputLayout(fsQuadLayout); + + UINT stride = sizeof(SimpleVertex); + UINT offset = 0; + + g_pDeviceContext->IASetVertexBuffers(0,1, &fsQuadBuffer, &stride, &offset); + g_pDeviceContext->Draw(4, 0); + } + + void RendererDX11::RenderToTextureNoPs() + { + g_pDeviceContext->VSSetShader(vsQuad, NULL, 0); + + g_pDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + g_pDeviceContext->IASetInputLayout(fsQuadLayout); + + UINT stride = sizeof(SimpleVertex); + UINT offset = 0; + + g_pDeviceContext->IASetVertexBuffers(0,1, &fsQuadBuffer, &stride, &offset); + g_pDeviceContext->Draw(4, 0); + } + + IVertexBuffer* RendererDX11::CreateVertexBuffer(int size, void* data, int stride) + { + return new IVertexBuffer11(g_pDevice, data, size, stride, false); + } + IVertexBuffer* RendererDX11::CreateVertexBufferDynamic(int size, int stride) + { + return new IVertexBuffer11(g_pDevice, NULL, size, stride, false); + } + IIndexBuffer* RendererDX11::CreateIndexBuffer(int size, void* data, int sixteenBit) + { + return new IIndexBuffer11(g_pDevice, data, size,sixteenBit, false); + } + IIndexBuffer* RendererDX11::CreateIndexBufferDynamic(int size, bool sixteenBit) + { + return new IIndexBuffer11(g_pDevice, NULL, size,sixteenBit, false); + } + IInputLayout* RendererDX11::CreateInputLayout(IInputElement* elements, int elementCount, IShader* vertexShader) + { + IVertexShader11* vs = (IVertexShader11*)vertexShader; + D3D11_INPUT_ELEMENT_DESC* desc = new D3D11_INPUT_ELEMENT_DESC[elementCount]; + HRESULT hr; + + for(int i = 0; i< elementCount; i++) + { + desc[i].SemanticName = elements[i].Name.c_str(); + desc[i].SemanticIndex = elements[i].SemanticIndex; + desc[i].InstanceDataStepRate = elements[i].InstancingRate; + desc[i].Format = (DXGI_FORMAT)elements[i].Format; + desc[i].InputSlot = elements[i].Slot; + desc[i].AlignedByteOffset = elements[i].Offset; + + if(elements[i].IsInstancingData) + desc[i].InputSlotClass = D3D11_INPUT_PER_INSTANCE_DATA; + else + desc[i].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + } + IInputLayout11* layout = new IInputLayout11(); + + hr = g_pDevice->CreateInputLayout(desc,elementCount, vs->g_bytecode->GetBufferPointer(), vs->g_bytecode->GetBufferSize(), &layout->Decl); + // if( FAILED( hr ) ) + // return hr; + return layout; + } + IShader* RendererDX11::CompileVertexShader(const char* pData, int length) + { + char* tmp = new char[length + 1]; + tmp[length] = 0; + memcpy(&tmp[0], pData, length); + + //printf("%s\n", tmp); + + IVertexShader11* vs = new IVertexShader11(g_pDevice); + vs->Compile(pData, length, NULL); + return vs; + } + IShader* RendererDX11::CompileVertexShader(LifeCore::IDataStreamPtr stream) + { + IVertexShader11* vs = new IVertexShader11(g_pDevice); + vs->Compile(stream, NULL); + return vs; + } + IShader* RendererDX11::CompileVertexShader(LifeCore::IDataStreamPtr stream, std::vector* defines) + { + IVertexShader11* vs = new IVertexShader11(g_pDevice); + vs->Compile(stream, defines); + return vs; + } + + IShader* RendererDX11::CompilePixelShader(const char* pData, int length) + { + IPixelShader11* ps = new IPixelShader11(g_pDevice); + ps->Compile(pData, length, NULL); + return ps; + } + IShader* RendererDX11::CompilePixelShader(LifeCore::IDataStreamPtr stream) + { + IPixelShader11* ps = new IPixelShader11(g_pDevice); + ps->Compile(stream, NULL); + return ps; + } + IShader* RendererDX11::CompilePixelShader(LifeCore::IDataStreamPtr stream, std::vector* defines) + { + IPixelShader11* ps = new IPixelShader11(g_pDevice); + ps->Compile(stream, defines); + return ps; + } + IConstantBuffer* RendererDX11::CreateConstantBuffer(int size) + { + return new IConstantBuffer11(g_pDevice, size); + } + + void RendererDX11::SetVSConstantBuffer(IConstantBuffer* buffer, int slot) + { + IConstantBuffer11* cb = (IConstantBuffer11*)buffer; + g_pDeviceContext->VSSetConstantBuffers(slot,1,&cb->buffer); + } + void RendererDX11::SetPSConstantBuffer(IConstantBuffer* buffer, int slot) + { + IConstantBuffer11* cb = (IConstantBuffer11*)buffer; + g_pDeviceContext->PSSetConstantBuffers(slot,1,&cb->buffer); + } + void RendererDX11::RenderMesh(IMesh* mesh) + { + IIndexBuffer11* ib = (IIndexBuffer11*)mesh->indexData; + IVertexBuffer11* vb = (IVertexBuffer11*)mesh->vertexData; + g_pDeviceContext->IASetIndexBuffer(ib->ib, DXGI_FORMAT_R16_UINT, 0); + UINT offset = 0; + g_pDeviceContext->IASetVertexBuffers(0,1, &vb->vb, &vb->stride, &offset); + SetInputLayout(mesh->inputLayout); + g_pDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + + for(int i = 0; isubsetCount; i++) + { + //IMaterial* mat = &mesh->subsets[i].material; + //SetMaterial(mat); + + g_pDeviceContext->DrawIndexed(mesh->subsets[i].Length * 3, mesh->subsets[i].Offset * 3, 0); + } + } + void RendererDX11::SetInputLayout(IInputLayout* layout) + { + IInputLayout11* il = (IInputLayout11*)layout; + g_pDeviceContext->IASetInputLayout(il->Decl); + } + + void RendererDX11::SetMaterial(IMaterial* material) + { + assert(false && "SetMaterial is obsolete!"); + /*if(material->diffuseMap.ptr()) + { + ITexture2D11* diffuse = (ITexture2D11*)material->diffuseMap.ptr(); + ID3D11ShaderResourceView* tex = static_cast(diffuse->getSRView())->view; + g_pDeviceContext->PSSetShaderResources(0,1,&tex); + }*/ + //IVertexShader11* vs = (IVertexShader11*)material->vertexShader; + // IPixelShader11* ps = (IPixelShader11*)material->pixelShader; + // g_pDeviceContext->PSSetShader(ps->g_shader,NULL, 0); + // g_pDeviceContext->VSSetShader(vs->g_shader,NULL, 0); + } + ITexture2D* RendererDX11::CreateDepthTarget(int width, int height, int format) + { + D3D11_TEXTURE2D_DESC desc; + ZeroMemory(&desc, sizeof(D3D11_TEXTURE2D_DESC)); + desc.ArraySize = 1; + desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; + desc.Format = (DXGI_FORMAT)format; + if(desc.Format == DXGI_FORMAT_R32_TYPELESS) + { + desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE; + } + + desc.CPUAccessFlags = 0; + + //desc.Format = DXGI_FORMAT_D32_FLOAT; + desc.MipLevels = 1; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Usage = D3D11_USAGE_DEFAULT; + + desc.Width = width; + desc.Height = height; + + ID3D11Texture2D* tex; + g_pDevice->CreateTexture2D(&desc, NULL, &tex); + + ITexture2D11* rTex = new ITexture2D11(g_pDevice); + rTex->pDevice = g_pDevice; + rTex->FromTexture(tex); + + ID3D11DepthStencilView* dt; + + D3D11_DEPTH_STENCIL_VIEW_DESC dsViewDesc; + if(desc.Format == DXGI_FORMAT_R32_TYPELESS) + dsViewDesc.Format = DXGI_FORMAT_D32_FLOAT; + else + dsViewDesc.Format = desc.Format; + dsViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS; + dsViewDesc.Texture2D.MipSlice = 0; + dsViewDesc.Flags = 0; + + g_pDevice->CreateDepthStencilView(tex, &dsViewDesc, &dt); + //g_pDevice->CreateRenderTargetView(tex, NULL, &dt); + + if(desc.Format == DXGI_FORMAT_R32_TYPELESS) + { + ID3D11ShaderResourceView* srv; + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; + srvDesc.Format = DXGI_FORMAT_R32_FLOAT; + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + srvDesc.Texture2D.MipLevels = 1; + srvDesc.Texture2D.MostDetailedMip = 0; + + g_pDevice->CreateShaderResourceView(tex,&srvDesc,&srv); + + IShaderResourceView11* srv11 = new IShaderResourceView11(srv); + rTex->addShaderResourceView(srv11); + } + rTex->pDevice = g_pDevice; + rTex->IsDepthTarget = true; + rTex->addDepthStencilView(dt); + return rTex; + } + + ITexture2D* RendererDX11::CreateDepthTargetCube(int width, int format) + { + D3D11_TEXTURE2D_DESC desc; + ZeroMemory(&desc, sizeof(D3D11_TEXTURE2D_DESC)); + desc.ArraySize = 6; + desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; + desc.CPUAccessFlags = 0; + desc.Format = (DXGI_FORMAT)format; + if(desc.Format == DXGI_FORMAT_R32_TYPELESS) + { + desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE; + } + desc.MipLevels = 1; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.Width = width; + desc.Height = desc.Width; + desc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE; + + //create depth texture + ID3D11Texture2D* tex; + g_pDevice->CreateTexture2D(&desc, NULL, &tex); + + ITexture2D11* rTex = new ITexture2D11(g_pDevice); + rTex->pDevice = g_pDevice; + rTex->FromTexture(tex); + + for(int i = 0; i<6; i++) + { + ID3D11DepthStencilView* dt; + + D3D11_DEPTH_STENCIL_VIEW_DESC dsv; + ZeroMemory(&dsv, sizeof(D3D11_DEPTH_STENCIL_VIEW_DESC)); + dsv.Format = desc.Format; + dsv.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY; + dsv.Texture2DArray.ArraySize = 1; + dsv.Texture2DArray.FirstArraySlice = i; + dsv.Texture2DArray.MipSlice = 0; + + //create depth-stencil view for 1-st cube map face + g_pDevice->CreateDepthStencilView(tex, &dsv, &dt); + + rTex->addDepthStencilView(dt); + } + + if(desc.Format == DXGI_FORMAT_R32_TYPELESS) + { + ID3D11ShaderResourceView* srv; + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; + srvDesc.Format = DXGI_FORMAT_R32_FLOAT; + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; + srvDesc.Texture2D.MipLevels = 1; + srvDesc.Texture2D.MostDetailedMip = 0; + + g_pDevice->CreateShaderResourceView(tex,&srvDesc,&srv); + + IShaderResourceView11* srv11 = new IShaderResourceView11(srv); + rTex->addShaderResourceView(srv11); + } + + rTex->pDevice = g_pDevice; + rTex->IsDepthTarget = true; + + return rTex; + } + + void RendererDX11::SetTargets(ITexture2D* colorTarget, ITexture2D* depthTarget) + { + ITexture2D11* depth = static_cast(depthTarget); + if(colorTarget) + { + ITexture2D11* color = static_cast(colorTarget); + IRenderTargetView11* rtv =static_cast(color->getRTView()); + g_pDeviceContext->OMSetRenderTargets(1, &rtv->view, depth->getDSView()); + } + else + { + g_pDeviceContext->OMSetRenderTargets(0, NULL, depth->getDSView()); + } + } + void RendererDX11::SetTargets(ISurface* colorTarget, ISurface* depthTarget) + { + ISurface11* depth = static_cast(depthTarget); + + if(colorTarget) + { + ISurface11* color = static_cast(colorTarget); + ID3D11RenderTargetView* rtv = color->getRTV(); + g_pDeviceContext->OMSetRenderTargets(1, &rtv, depth->getDSV()); + } + else + { + g_pDeviceContext->OMSetRenderTargets(0, NULL, depth->getDSV()); + } + } + + void RendererDX11::SetCubeTarget(ITexture2D* colorTarget, ITexture2D* depthTarget, int faceIndex) + { + ITexture2D11* depth = static_cast(depthTarget); + if(colorTarget) + { + ITexture2D11* color = static_cast(colorTarget); + IRenderTargetView11* rtv =static_cast(color->getRTView(faceIndex)); + g_pDeviceContext->OMSetRenderTargets(1, &rtv->view, depth->getDSView(faceIndex)); + } + else + { + g_pDeviceContext->OMSetRenderTargets(0, NULL, depth->getDSView(faceIndex)); + } + } + + void RendererDX11::SetTargets(ITexture2D** colorTargets, int targetCount, ITexture2D* depthTarget) + { + ITexture2D11* depth = static_cast(depthTarget); + static ID3D11RenderTargetView* rtv[8] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; + + for(int i = 0; i < targetCount; i++) + { + ITexture2D11* color = static_cast(colorTargets[i]); + rtv[i] = static_cast(color->getRTView())->view; + } + + g_pDeviceContext->OMSetRenderTargets(targetCount, rtv, depth->getDSView()); + } + + void RendererDX11::UnbindRenderTargets() + { + ID3D11RenderTargetView* const rtv[8] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; + g_pDeviceContext->OMSetRenderTargets(8, rtv, NULL); + } + + void RendererDX11::SetColorTarget(ITexture2D* colorTarget) + { + ITexture2D11* color = static_cast(colorTarget); + IRenderTargetView11* rtv =static_cast(color->getRTView()); + assert(false && "Fix that"); + //g_pDeviceContext->OMSetRenderTargets(1, &rtv->view, currentDepthBuffer); + } + void RendererDX11::SetDepthTarget(ITexture2D* depthTarget) + { + ITexture2D11* depth = static_cast(depthTarget); + assert(false && "Fix that"); + //g_pDeviceContext->OMSetRenderTargets(1, ¤tBackBuffer, depth->getDSView()); + } + + void RendererDX11::SetVertexBuffer(IVertexBuffer* buffer) + { + IVertexBuffer11* vb = static_cast(buffer); + UINT offset = 0; + g_pDeviceContext->IASetVertexBuffers(0,1, &vb->vb, &vb->stride, &offset); + } + + void RendererDX11::SetVertexBuffer(IVertexBuffer** vb, int count) + { + UINT strides[4]; + UINT offsets[4] = {0,0,0,0}; + ID3D11Buffer* buf[4]; + for(int i = 0; i< count; i++) + { + IVertexBuffer11* b = static_cast(vb[i]); + buf[i] = b->vb; + strides[i] = b->stride; + } + g_pDeviceContext->IASetVertexBuffers(0,count, buf, strides, offsets); + } + + void RendererDX11::SetIndexBuffer(IIndexBuffer* buffer) + { + IIndexBuffer11* ib = static_cast(buffer); + DXGI_FORMAT fmt = DXGI_FORMAT_R16_UINT; + if(!ib->isShort) + fmt = DXGI_FORMAT_R32_UINT; + + g_pDeviceContext->IASetIndexBuffer(ib->ib, fmt, 0); + } + void RendererDX11::SetPrimitiveTopology(PRIMITIVE_TOPOLOGY topology) + { + g_pDeviceContext->IASetPrimitiveTopology((D3D11_PRIMITIVE_TOPOLOGY)topology); + } + void RendererDX11::DrawIndexed(int offset, int length) + { + g_pDeviceContext->DrawIndexed(length * 3, offset * 3, 0); + } + void RendererDX11::DrawIndexed(int offset, int length, int baseVertex) + { + g_pDeviceContext->DrawIndexed(length * 3, offset * 3, baseVertex); + } + + void RendererDX11::DrawIndexedInstanced(int offset, int length, int instanceCnt) + { + g_pDeviceContext->DrawIndexedInstanced(length * 3, instanceCnt, offset * 3, 0, 0); + } + + void RendererDX11::DrawIndexedInstanced(int offset, int length, int baseVertex, int instanceCnt) + { + g_pDeviceContext->DrawIndexedInstanced(length * 3, instanceCnt, offset * 3, baseVertex,0); + } + + void RendererDX11::SetVertexShader(IShader* shader) + { + IVertexShader11* vs = static_cast(shader); + g_pDeviceContext->VSSetShader(vs->g_shader, NULL, 0); + } + void RendererDX11::SetPixelShader(IShader* shader) + { + IPixelShader11* ps = static_cast(shader); + g_pDeviceContext->PSSetShader(ps->g_shader, NULL, 0); + } + void RendererDX11::SetPSTexture(ITexture2D* texture, int slot) + { + IShaderResourceView11* tex = static_cast(texture->getSRView()); + g_pDeviceContext->PSSetShaderResources(slot,1,&tex->view); + } + void RendererDX11::SetPSTextureCube(ITexture2D* texture, int slot) + { + ID3D11ShaderResourceView* tex = static_cast(texture)->cubeResourceView; + g_pDeviceContext->PSSetShaderResources(slot,1,&tex); + } + void RendererDX11::SetPSTexture(ITexture2D* texture, ITexture2D* texture2, int start, int count) + { + ID3D11ShaderResourceView* views[2]; + views[0] = static_cast(texture->getSRView())->view; + views[1] = static_cast(texture2->getSRView())->view; + + //IShaderResourceView11* tex = static_cast(texture->getSRView()); + g_pDeviceContext->PSSetShaderResources(start,2, views); + } + void RendererDX11::SetVSTexture(ITexture2D* texture, int slot) + { + IShaderResourceView11* tex = static_cast(texture->getSRView()); + g_pDeviceContext->VSSetShaderResources(slot,1,&tex->view); + } + void RendererDX11::UnbindResourcesPS() + { + ID3D11ShaderResourceView *const pSRV[10] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; + + g_pDeviceContext->PSSetShaderResources(0, 10, pSRV); + } + void RendererDX11::Draw(int vertexCount, int vertexOffset) + { + g_pDeviceContext->Draw(vertexCount, vertexOffset); + } + + IBlendState* RendererDX11::CreateBlendState(bool blendEnable, bool atocEnable, BLEND_OP blendOperation, BLEND_SRC srcBlend, BLEND_SRC dstBlend, uint8 colorMask) + { + ID3D11BlendState* state; + D3D11_BLEND_DESC desc = {0}; + desc.AlphaToCoverageEnable = atocEnable; + desc.IndependentBlendEnable = false; + desc.RenderTarget[0].BlendEnable = blendEnable; + desc.RenderTarget[0].BlendOp = static_cast(blendOperation); + desc.RenderTarget[0].BlendOpAlpha = static_cast(blendOperation); + desc.RenderTarget[0].SrcBlend = static_cast(srcBlend); + desc.RenderTarget[0].DestBlend = static_cast(dstBlend); + desc.RenderTarget[0].RenderTargetWriteMask = colorMask; + desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE; + desc.RenderTarget[0].SrcBlendAlpha= D3D11_BLEND_ONE; + g_pDevice->CreateBlendState(&desc,&state); + + return new IBlendState11(g_pDevice, state); + } + void RendererDX11::SetBlendState(IBlendState* state) + { + IBlendState11* s = static_cast(state); + + LifeMath::float4 BlendFactor( 1.0f, 1.0f, 1.0f, 1.0f ); + g_pDeviceContext->OMSetBlendState(s->state, (const FLOAT*) &BlendFactor, 0xffffffff); + } + IDepthStencilState* RendererDX11::CreateDSState(bool zEnable, bool zWrite, COMPARISON_FUNC zFunc) + { + D3D11_DEPTH_STENCIL_DESC State; + State.DepthEnable = zEnable; + + if(zWrite) + State.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; + else + State.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; + + State.DepthFunc = static_cast(zFunc); + State.StencilEnable = FALSE; + State.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK; + State.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK; + + State.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; + State.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; + State.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; + State.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; + + State.BackFace.StencilFunc = D3D11_COMPARISON_NEVER; + State.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; + State.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; + State.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; + + ID3D11DepthStencilState* s; + g_pDevice->CreateDepthStencilState(&State, &s); + return new IDepthStencilState11(g_pDevice, s); + } + + IDepthStencilState* RendererDX11::CreateDSState(bool zEnable, bool zWrite, COMPARISON_FUNC zFunc, bool stencil, int stencilMask, + int stencilFunc, int stencilFail, int depthFail, int depthFailBack, int stencilPass) + { + D3D11_DEPTH_STENCIL_DESC State; + State.DepthEnable = zEnable; + + if(zWrite) + State.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; + else + State.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; + + State.DepthFunc = static_cast(zFunc); + State.StencilEnable = stencil; + State.StencilReadMask = stencilMask; + State.StencilWriteMask = stencilMask; + + State.FrontFace.StencilFunc = (D3D11_COMPARISON_FUNC)stencilFunc; + State.FrontFace.StencilPassOp = (D3D11_STENCIL_OP)stencilPass; + State.FrontFace.StencilFailOp = (D3D11_STENCIL_OP)stencilFail; + State.FrontFace.StencilDepthFailOp = (D3D11_STENCIL_OP)depthFail; + + State.BackFace.StencilFunc = (D3D11_COMPARISON_FUNC)stencilFunc; + State.BackFace.StencilPassOp = (D3D11_STENCIL_OP)stencilPass; + State.BackFace.StencilFailOp = (D3D11_STENCIL_OP)stencilFail; + State.BackFace.StencilDepthFailOp = (D3D11_STENCIL_OP)depthFailBack; + + ID3D11DepthStencilState* s; + g_pDevice->CreateDepthStencilState(&State, &s); + return new IDepthStencilState11(g_pDevice, s); + } + + void RendererDX11::SetDepthStencilState(IDepthStencilState* state, UINT sref) + { + IDepthStencilState11* ds = static_cast(state); + g_pDeviceContext->OMSetDepthStencilState(ds->state, sref); + } + + void RendererDX11::SetScissorRectangle(const LifeMath::Rect2i& rect) + { + g_pDeviceContext->RSSetState(rState); + D3D11_RECT sr[1]; + sr[0].top = (LONG)rect.y; + sr[0].bottom = (LONG)rect.GetMaxY(); + sr[0].left = (LONG)rect.x; + sr[0].right = (LONG)rect.GetMaxX(); + + g_pDeviceContext->RSSetScissorRects(1, sr); + } + void RendererDX11::SetViewport(const LifeMath::Rect2i& rect) + { + D3D11_VIEWPORT vp; + vp.Height = static_cast(rect.height); + vp.Width = static_cast(rect.width); + vp.TopLeftX = static_cast(rect.x); + vp.TopLeftY = static_cast(rect.y); + vp.MinDepth = 0.0f; + vp.MaxDepth = 1.0f; + g_pDeviceContext->RSSetViewports(1, &vp); + } + + void RendererDX11::SetViewport(const LifeMath::Rect2i& rect, float minDepth, float maxDepth) + { + D3D11_VIEWPORT vp; + vp.Height = static_cast(rect.height); + vp.Width = static_cast(rect.width); + vp.TopLeftX = static_cast(rect.x); + vp.TopLeftY = static_cast(rect.y); + vp.MinDepth = minDepth; + vp.MaxDepth = maxDepth; + g_pDeviceContext->RSSetViewports(1, &vp); + } + ITexture2D* RendererDX11::CreateRenderTargetCube(int size, int format) + { + ID3D11Texture2D* tex; + + D3D11_TEXTURE2D_DESC dstex; + ZeroMemory(&dstex, sizeof(D3D11_TEXTURE2D_DESC)); + dstex.Width = size; + dstex.Height = size; + dstex.MipLevels = 1; + dstex.ArraySize = 6; + dstex.SampleDesc.Count = 1; + dstex.SampleDesc.Quality = 0; + dstex.Format = static_cast(format); + dstex.Usage = D3D11_USAGE_DEFAULT; + dstex.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; + dstex.CPUAccessFlags = 0; + dstex.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE; + g_pDevice->CreateTexture2D( &dstex, NULL, &tex ); + + ITexture2D11* newTex = new ITexture2D11(g_pDevice); + newTex->FromTexture(tex); + + D3D11_RENDER_TARGET_VIEW_DESC rDesc; + rDesc.Format = dstex.Format; + rDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; + rDesc.Texture2DArray.ArraySize = 1; + rDesc.Texture2DArray.MipSlice = 0; + rDesc.Texture2DArray.FirstArraySlice = 0; + + D3D11_SHADER_RESOURCE_VIEW_DESC rvDesc; + ZeroMemory(&rvDesc, sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC)); + rvDesc.Format = dstex.Format; + rvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; + rvDesc.TextureCube.MipLevels = 1; + rvDesc.TextureCube.MostDetailedMip = 0; + ID3D11ShaderResourceView* srView; + g_pDevice->CreateShaderResourceView(tex, &rvDesc, &srView); + newTex->addShaderResourceView(new IShaderResourceView11(srView)); + + // Create the one-face renderTarget views + for( int i = 0; i < 6; i++ ) + { + ID3D11RenderTargetView* rtView; + rDesc.Texture2DArray.FirstArraySlice = i; + g_pDevice->CreateRenderTargetView( tex, &rDesc, &rtView ); + newTex->addRenderTargetView(new IRenderTargetView11(rtView)); + } + + //create 6-face shader resource view + ID3D11ShaderResourceView* fullView; + D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc; + ZeroMemory( &SRVDesc, sizeof( SRVDesc ) ); + SRVDesc.Format = dstex.Format; + SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; + SRVDesc.TextureCube.MipLevels = 1; + SRVDesc.TextureCube.MostDetailedMip = 0; + g_pDevice->CreateShaderResourceView(tex, &SRVDesc, &fullView); + newTex->cubeResourceView = fullView; + return newTex; + } + + ITextureSampler* RendererDX11::CreateTextureSampler(ISamplerDescription* desc) + { + return new ITextureSampler11(desc, g_pDevice); + } + + void RendererDX11::VerticalBlur(ITexture2D* src, ITexture2D* dst, int numSamples) + { + SetColorTarget(dst); + SetPSTexture(src, 0); + + g_pDeviceContext->PSSetShader(psVerticalBlur, NULL, 0); + + LifeMath::float4* offsets4 = new LifeMath::float4[numSamples]; + GetBlurWeightedOffsets((float)numSamples, dst->Height(), offsets4); + //g_verticalBlurPS.SetFloat4A("vBlurOffsets", offsets4); + + vbBuffer->Update(offsets4, 0); + SetPSConstantBuffer(vbBuffer, 0); + + RenderToTextureNoPs(); + delete[] offsets4; + } +} \ No newline at end of file diff --git a/LifeGraphics/render/dx11/RendererDX11.h b/LifeGraphics/render/dx11/RendererDX11.h new file mode 100644 index 0000000..0221fce --- /dev/null +++ b/LifeGraphics/render/dx11/RendererDX11.h @@ -0,0 +1,213 @@ +#ifndef RendererDX11_h__ +#define RendererDX11_h__ + +#include "Globals.h" +#include +#include "math/LifeMath.h" +#include +#include "ITexture2D11.h" +#include +#include "render/dx11/IVertexBuffer11.h" +#include "render/dx11/IIndexBuffer11.h" +#include "Globals.h" + +#include "render/RenderParams.h" +#include +#include +#include +#include +#include "render/dx11/IVertexShader11.h" +#include "render/dx11/IPixelShader11.h" +#include "render/dx11/IInputLayout11.h" +#include "render/dx11/IBlendState11.h" +#include "math/LifeMath.h" +#include "render/dx11/IRasterizerState11.h" +#include "render/dx11/ITextureSampler11.h" +#include "render/dx11/ISurface11.h" + +#include "render/dx11/ISwapChain11.h" + +namespace LifeGraphics +{ + struct SimpleVertex + { + LifeMath::float4 Pos; + LifeMath::float2 TC; + }; + class RendererDX11 : public IRenderer + { + public: + RendererDX11(){}; + ~RendererDX11(){printf("Destroying renderer...");Close();}; + virtual HRESULT Open(RenderParams params); + virtual void Close(); + //virtual void ClearBuffers(int flags, int rtColor, float zValue, int stencilValue); + virtual const String& GetRendererName() const { + static String sName("DirectX11 graphics driver"); + return sName; + } + + void ClearRenderTarget(ISurface* target, LifeMath::float4* color); + void ClearDepthStencil(ISurface* target, int flag, float zValue, int stencilValue); + + ITexture2D* CreateRenderTarget(int width, int height, int format); + ITexture2D* CreateDepthTarget(int width, int height, int format); + ITexture2D* LoadTexture2D(LifeCore::IDataStreamPtr src); + ITexture2D* CreateTexture2D(int canRead, int canWrite, int width, int height, int format); + void RenderToTexture(); + void RenderToTextureNoPs(); + void RenderToTextureNoShader(); + IVertexBuffer* CreateVertexBuffer(int size, void* data, int stride); + IVertexBuffer* CreateVertexBufferDynamic(int size, int stride); + IIndexBuffer* CreateIndexBuffer(int size, void* data, int sixteenBit); + IIndexBuffer* CreateIndexBufferDynamic(int size, bool sixteenBit); + IInputLayout* CreateInputLayout(IInputElement* elements, int elementCount, IShader* vertexShader); + + IShader* CompileVertexShader(const char* pData, int length); + IShader* CompileVertexShader(LifeCore::IDataStreamPtr stream); + IShader* CompileVertexShader(LifeCore::IDataStreamPtr stream, std::vector* defines); + + IShader* CompilePixelShader(const char* pData, int length); + IShader* CompilePixelShader(LifeCore::IDataStreamPtr stream); + IShader* CompilePixelShader(LifeCore::IDataStreamPtr stream, std::vector* defines); + + IConstantBuffer* CreateConstantBuffer(int size); + + void SetVSConstantBuffer(IConstantBuffer* buffer, int slot); + void SetPSConstantBuffer(IConstantBuffer* buffer, int slot); + + void SetInputLayout(IInputLayout* layout); + void RenderMesh(IMesh* mesh); + + void SetMaterial(IMaterial* material); + + void SetTargets(ITexture2D* colorTarget, ITexture2D* depthTarget); + void SetTargets(ISurface* colorTarget, ISurface* depthTarget); + void SetCubeTarget(ITexture2D* colorTarget, ITexture2D* depthTarget, int faceIndex); + + void SetVertexBuffer(IVertexBuffer* vb); + void SetIndexBuffer(IIndexBuffer* ib); + + void SetPrimitiveTopology(PRIMITIVE_TOPOLOGY topology); + void SetPSTextureCube(ITexture2D* texture, int slot); + void DrawIndexed(int offset, int length); + + void SetVertexShader(IShader* shader); + void SetPixelShader(IShader* shader); + + void SetPSTexture(ITexture2D* texture, int slot); + void SetVSTexture(ITexture2D* texture, int slot); + + void UnbindResourcesPS(); + + void Draw(int vertexCount, int vertexOffset); + + IBlendState* CreateBlendState(bool blendEnable, bool atocEnable, BLEND_OP blendOperation, BLEND_SRC srcBlend, BLEND_SRC dstBlend, uint8 colorMask); + + void SetBlendState(IBlendState* state); + + IDepthStencilState* CreateDSState(bool zEnable, bool zWrite, COMPARISON_FUNC zFunc); + + IDepthStencilState* CreateDSState(bool zEnable, bool zWrite, COMPARISON_FUNC zFunc, bool stencil, int stencilMask, int stencilFunc, int stencilFail, int depthFail, int depthFailBack,int stencilPass); + + void SetDepthStencilState(IDepthStencilState* state, UINT sref = 0); + + void SetScissorRectangle(const LifeMath::Rect2i& rect); + + void SetViewport(const LifeMath::Rect2i& rect); + + ITexture2D* CreateRenderTargetCube(int size, int format); + + ITexture2D* CreateDepthTargetCube(int width, int format); + + void SetPSTextureNULL(int slot); + + void SetPSTextureSampler(ITextureSampler* sampler, int slot); + + ITextureSampler* CreateTextureSampler(ISamplerDescription* desc); + + void SetColorTarget(ITexture2D* colorTarget); + void SetDepthTarget(ITexture2D* depthTarget); + + void VerticalBlur(ITexture2D* src, ITexture2D* dst, int numSamples); + + void SetPSTexture(ITexture2D* texture, ITexture2D* texture2, int start, int count); + + IShader* CompileUserShaderPS(char* data); + + IVertexBuffer11* testVb; + + IRasterizerState* CreateRasterizerState(FILL_MODE mode); + + void DrawIndexed(int offset, int length, int baseVertex); + + void SetRasterizerState(IRasterizerState* state); + + void SetTargets(ITexture2D** colorTargets, int targetCount, ITexture2D* depthTarget); + + void UnbindRenderTargets(); + + void SetVertexBuffer(IVertexBuffer** vb, int count); + + void DrawIndexedInstanced(int offset, int length, int instanceCnt); + + void DrawIndexedInstanced(int offset, int length, int baseVertex, int instanceCnt); + + void SetViewport(const LifeMath::Rect2i& rect, float minDepth, float maxDepth); + + IRasterizerState* CreateRasterizerState(FILL_MODE mode, CULL_MODE cmode); + + void DownSample2x2(ITexture2D* src, ITexture2D* dst, ITexture2D* depth); + void BlurGaussH(ITexture2D* src, ITexture2D* dst, ITexture2D* depth); + void BlurGaussV(ITexture2D* src, ITexture2D* dst, ITexture2D* depth); + + void BeginCleanup(); + void EndCleanup(); + + void ClearRenderTarget(ITexture2D* tex, LifeMath::float4* color); + void ClearDepthStencil(ITexture2D* tex, float zValue, int stencilValue); + void ClearDepthStencil(ITexture2D* tex, int flag, float zValue, int stencilValue); + + int GetDisplayModes(LifeCore::DisplayMode** modeList); + + ISwapChain* CreateSwapChain(LifeCore::IRenderWindow* window); + private: + + IShader* ds2x2; + IConstantBuffer* ds2x2cb; + + IDXGIAdapter* pDXGIAdapter; + IDXGIDevice* pDXGIDevice; + IDXGIOutput* pDXGIOutput; + + ID3D11RasterizerState* rState; + ID3D11Device* g_pDevice; + ID3D11DeviceContext* g_pDeviceContext; + //IDXGISwapChain* g_pSwapChain; + + //ID3D11RenderTargetView* g_pBackBuffer; + //ID3D11DepthStencilView* g_pDepthBuffer; + + //ID3D11RenderTargetView* currentBackBuffer; + //ID3D11DepthStencilView* currentDepthBuffer; + + D3D_FEATURE_LEVEL g_pFeatureLevel; + D3D_DRIVER_TYPE g_driverType; + ID3D11Buffer* fsQuadBuffer; + ID3D11PixelShader* psQuad; + ID3D11VertexShader* vsQuad; + ID3D11InputLayout* fsQuadLayout; + ID3D11SamplerState* samplerLinear; + ID3D11SamplerState* samplerPoint; + //ID3D11Texture2D* pBuffer; + //ID3D11Texture2D* dBuf; + //Camera* cam; + + ID3D11PixelShader* psVerticalBlur; + + IConstantBuffer* vbBuffer; + + IDXGIFactory* pFactory; + }; +} +#endif // RendererDX11_h__ \ No newline at end of file diff --git a/LifeGraphics/render/software/IBlendStateS.h b/LifeGraphics/render/software/IBlendStateS.h new file mode 100644 index 0000000..e69de29 diff --git a/LifeGraphics/render/software/IConstantBufferS.h b/LifeGraphics/render/software/IConstantBufferS.h new file mode 100644 index 0000000..e69de29 diff --git a/LifeGraphics/render/software/IDepthStencilStateS.h b/LifeGraphics/render/software/IDepthStencilStateS.h new file mode 100644 index 0000000..e69de29 diff --git a/LifeGraphics/render/software/IIndexBufferS.h b/LifeGraphics/render/software/IIndexBufferS.h new file mode 100644 index 0000000..e69de29 diff --git a/LifeGraphics/render/software/IInputLayoutS.h b/LifeGraphics/render/software/IInputLayoutS.h new file mode 100644 index 0000000..e69de29 diff --git a/LifeGraphics/render/software/IPixelShaderS.h b/LifeGraphics/render/software/IPixelShaderS.h new file mode 100644 index 0000000..e69de29 diff --git a/LifeGraphics/render/software/IRasterizerStateS.h b/LifeGraphics/render/software/IRasterizerStateS.h new file mode 100644 index 0000000..e69de29 diff --git a/LifeGraphics/render/software/IRenderTargetViewS.h b/LifeGraphics/render/software/IRenderTargetViewS.h new file mode 100644 index 0000000..e69de29 diff --git a/LifeGraphics/render/software/IShaderResourceViewS.h b/LifeGraphics/render/software/IShaderResourceViewS.h new file mode 100644 index 0000000..e69de29 diff --git a/LifeGraphics/render/software/ITexture2DS.h b/LifeGraphics/render/software/ITexture2DS.h new file mode 100644 index 0000000..e69de29 diff --git a/LifeGraphics/render/software/ITextureSamplerS.h b/LifeGraphics/render/software/ITextureSamplerS.h new file mode 100644 index 0000000..e69de29 diff --git a/LifeGraphics/render/software/IVertexBufferS.h b/LifeGraphics/render/software/IVertexBufferS.h new file mode 100644 index 0000000..e69de29 diff --git a/LifeGraphics/render/software/IVertexShaderS.h b/LifeGraphics/render/software/IVertexShaderS.h new file mode 100644 index 0000000..e69de29 diff --git a/LifeGraphics/render/software/RendererSoftware.cpp b/LifeGraphics/render/software/RendererSoftware.cpp new file mode 100644 index 0000000..09672a6 --- /dev/null +++ b/LifeGraphics/render/software/RendererSoftware.cpp @@ -0,0 +1,14 @@ +#pragma once +#include "render/RendererSoftware.h" + +#include "vfs/fs_win/IFSContentProvider.h" +#include "LString.h" +#include "vfs/IFile.h" +#include +#include "render/software/IConstantBufferS.h" +#include "render/IMaterial.h" +#include "render/software/IDepthStencilStateS.h" + +namespace LifeGraphics +{ +} \ No newline at end of file diff --git a/LifeGraphics/renderpath/IRenderPath.h b/LifeGraphics/renderpath/IRenderPath.h new file mode 100644 index 0000000..96368c9 --- /dev/null +++ b/LifeGraphics/renderpath/IRenderPath.h @@ -0,0 +1,21 @@ +#ifndef IRenderPath_h__ +#define IRenderPath_h__ + +#include "CG.h" + +namespace LifeGraphics +{ + class IScene; + namespace RenderPath + { + class IRenderPath + { + public: + virtual ~IRenderPath() {} + virtual void Render(LifeGraphics::IScene* pScene) = 0; + protected: + IRenderer* pRenderer; + }; + } +} +#endif // IRenderPath_h__ \ No newline at end of file diff --git a/LifeGraphics/renderpath/IRenderPathShadow.h b/LifeGraphics/renderpath/IRenderPathShadow.h new file mode 100644 index 0000000..7526d4f --- /dev/null +++ b/LifeGraphics/renderpath/IRenderPathShadow.h @@ -0,0 +1,28 @@ +#ifndef IRenderPathShadow_h__ +#define IRenderPathShadow_h__ + +#include "renderpath/IRenderPath.h" +#include "Light.h" +#include "ShadowMapManager.h" +#include "queries/IFrustumQuery.h" + +namespace LifeGraphics { namespace RenderPath +{ + class IRenderPathShadow : public IRenderPath + { + public: + IRenderPathShadow(ShadowMapManager* sm) + { + pShadowManager = sm; + } + virtual ~IRenderPathShadow() {} + virtual void SetLight(const Light& _light) = 0; + protected: + Light light; + ShadowMapManager* pShadowManager; + IConstantBuffer* cbCamera; + IConstantBuffer* cbLight; + IFrustumQuery frustumQuery; + }; +}} +#endif // IRenderPathShadow_h__ \ No newline at end of file diff --git a/LifeGraphics/renderpath/RenderPathDefault.cpp b/LifeGraphics/renderpath/RenderPathDefault.cpp new file mode 100644 index 0000000..12214a8 --- /dev/null +++ b/LifeGraphics/renderpath/RenderPathDefault.cpp @@ -0,0 +1,4 @@ +#ifndef RenderPathDefault_h__ +#define RenderPathDefault_h__ + +#endif // RenderPathDefault_h__ \ No newline at end of file diff --git a/LifeGraphics/renderpath/RenderPathDefault.h b/LifeGraphics/renderpath/RenderPathDefault.h new file mode 100644 index 0000000..af8e3a1 --- /dev/null +++ b/LifeGraphics/renderpath/RenderPathDefault.h @@ -0,0 +1,16 @@ +#ifndef RenderPathDefault_h__ +#define RenderPathDefault_h__ + +#include "renderpath/IRenderPath.h" + +namespace LifeGraphics { namespace RenderPath +{ + class RenderPathDefault : public IRenderPath + { + public: + RenderPathDefault(); + ~RenderPathDefault(); + void Render(IScene* pScene); + }; +}} +#endif // RenderPathDefault_h__ \ No newline at end of file diff --git a/LifeGraphics/renderpath/RenderPathDeferred.h b/LifeGraphics/renderpath/RenderPathDeferred.h new file mode 100644 index 0000000..9c574bd --- /dev/null +++ b/LifeGraphics/renderpath/RenderPathDeferred.h @@ -0,0 +1,4 @@ +#ifndef RenderPathDeferred_h__ +#define RenderPathDeferred_h__ + +#endif // RenderPathDeferred_h__ \ No newline at end of file diff --git a/LifeGraphics/renderpath/RenderPathForward.h b/LifeGraphics/renderpath/RenderPathForward.h new file mode 100644 index 0000000..aa66eef --- /dev/null +++ b/LifeGraphics/renderpath/RenderPathForward.h @@ -0,0 +1,4 @@ +#ifndef RenderPathForward_h__ +#define RenderPathForward_h__ + +#endif // RenderPathForward_h__ \ No newline at end of file diff --git a/LifeGraphics/renderpath/RenderPathOmniShadow.cpp b/LifeGraphics/renderpath/RenderPathOmniShadow.cpp new file mode 100644 index 0000000..500baf9 --- /dev/null +++ b/LifeGraphics/renderpath/RenderPathOmniShadow.cpp @@ -0,0 +1,128 @@ +#include "renderpath/RenderPathOmniShadow.h" + +#include "IScene.h" + +namespace LifeGraphics { namespace RenderPath +{ + RenderPathOmniShadow::RenderPathOmniShadow(ShadowMapManager* sm) + :IRenderPathShadow(sm) + { + pRenderer = CG::me()->GetRenderer(); + renderTarget = pRenderer->CreateRenderTargetCube(512, FORMAT_R32_FLOAT); + + depthTarget = pRenderer->CreateDepthTargetCube(512, FORMAT_D24_UNORM_S8_UINT); + cbCamera = pRenderer->CreateConstantBuffer(sizeof(PER_CAMERA_CONSTANTS)); + + //cbLight = pRenderer->CreateConstantBuffer(sizeof(POINT_LIGHT_DATA)); + } + RenderPathOmniShadow::~RenderPathOmniShadow() + { + } + void RenderPathOmniShadow::SetLight(const Light& _light) + { + light = _light; + } + void RenderPathOmniShadow::Render(IScene* pScene) + { + //top, bottom, left, right, front, back directions + static LifeMath::float3 dir[6] = + { + LifeMath::float3( 1, 0, 0), + LifeMath::float3( -1, 0, 0), + + LifeMath::float3( 0, 1, 0), + LifeMath::float3( 0, -1, 0), + + LifeMath::float3( 0, 0, 1), + LifeMath::float3( 0, 0, -1), + }; + + LifeMath::float3 lightPos = light.getTransform()->GetPosition(); + + //compute 6 cube face cameras + for(int i = 0; i < 6; i++) + { + cam[i].setPosition(lightPos); + cam[i].setDirection(dir[i] + lightPos); + cam[i].setFov(3.1415f * 0.5f); + cam[i].setAspect(1.0f); + cam[i].setNear(0.1f); + cam[i].setFar(light.getRange()); + } + + LifeMath::Rect2i vp(0,0,512,512); + pRenderer->SetViewport(vp); + + //for render scene depth to 6 cube faces + for(int i = 0; i < 6; i++) + { + //pRenderer->SetCubeTarget(NULL, depthTarget, i); + //pRenderer->ClearRenderTarget() + static LifeMath::float4 clearColor(0,0,0,0); + pRenderer->ClearDepthStencil(depthTarget->getSurface(i), 1, 1.0f, 0); + pRenderer->ClearRenderTarget(renderTarget->getSurface(i), &clearColor); + pRenderer->SetTargets(renderTarget->getSurface(i), depthTarget->getSurface(i)); + + frustumQuery.SetFrustum(*cam[i].getFrustum()); + frustumQuery.Filration = true; + frustumQuery.filter = NT_MESH; + //get all visible objects + pScene->PerformQuery(&frustumQuery); + + int nc = frustumQuery.Size(); + + PER_CAMERA_CONSTANTS pccs = {0}; + + pccs.camNearFar = LifeMath::float4(cam[i].getNear(), cam[i].getFar(), 0.0f,0.0f); + pccs.mView = cam[i].getView(); + pccs.mProj = cam[i].getProj(); + pccs.mInvVP = pccs.mView * pccs.mProj; + LifeMath::float3 camPosl = cam[i].getPosition(); + LifeMath::float3 camDirl = cam[i].getDirection(); + pccs.camPos = camPosl; + + pccs.camDir = camDirl; + + cbCamera->Update(&pccs, sizeof(PER_CAMERA_CONSTANTS)); + pRenderer->SetVSConstantBuffer(cbCamera, 1); + pRenderer->SetPSConstantBuffer(cbCamera, 1); + + pRenderer->SetDepthStencilState(pShadowManager->depthState); + + for(int j = 0; j < nc; j++) + { + IObject* mesh = static_cast(frustumQuery[j]); + mesh->bind(); + + for(int k = 0; k < mesh->getSumbeshCount(); k++) + { + IMaterialProxy* mat = mesh->getMaterial(k); + mat->SelectTechnique(MT_RENDER_DEFERRED); + mat->GetTechnique()->SelectPass(MP_DEPTH_SM); + + IMaterialPass* pass = mat->GetTechnique()->GetPass(); + pass->Apply(); + //pRenderer->SetVertexShader(mat->g_buffer_pass_vs); + + /*if(mat->diffuseMap.ptr()) + { + pRenderer->SetPSTexture(mat->diffuseMap.ptr(), 0); + } + if(mat->AlphaTest || mat->AlphaBlendEnable) + { + pRenderer->SetPixelShader(pShadowManager->rtdCubePsAT); + } + else + { + pRenderer->SetPixelShader(pShadowManager->rtdCubePs); + }*/ + mesh->render(k); + } + } + frustumQuery.Flush(); + //IGraphicsNode* n = frustumQuery[0]-> + } + + // renderTarget->ToFile("C:\\cube_shadow.dds"); + } +}} \ No newline at end of file diff --git a/LifeGraphics/renderpath/RenderPathOmniShadow.h b/LifeGraphics/renderpath/RenderPathOmniShadow.h new file mode 100644 index 0000000..ed06457 --- /dev/null +++ b/LifeGraphics/renderpath/RenderPathOmniShadow.h @@ -0,0 +1,27 @@ +#ifndef RenderPathOmniShadow_h__ +#define RenderPathOmniShadow_h__ + +#include "renderpath/IRenderPathShadow.h" + +#include "constants.h" + +namespace LifeGraphics { namespace RenderPath +{ + class RenderPathOmniShadow : public IRenderPathShadow + { + public: + RenderPathOmniShadow(ShadowMapManager* sm); + ~RenderPathOmniShadow(); + void SetLight(const Light& _light); + void Render(IScene* pScene); + ITexture2D* GetShadowMap() + { + return renderTarget; + } + protected: + Camera cam[6]; + ITexture2D* depthTarget; + ITexture2D* renderTarget; + }; +}} +#endif // RenderPathOmniShadow_h__ \ No newline at end of file diff --git a/LifeGraphics/renderpath/RenderPathSpotShadow.cpp b/LifeGraphics/renderpath/RenderPathSpotShadow.cpp new file mode 100644 index 0000000..08e3633 --- /dev/null +++ b/LifeGraphics/renderpath/RenderPathSpotShadow.cpp @@ -0,0 +1,102 @@ +#include "renderpath/RenderPathSpotShadow.h" +#include "IScene.h" + +namespace LifeGraphics +{ + namespace RenderPath + { + RenderPathSpotShadow::RenderPathSpotShadow(ShadowMapManager* sm) + :IRenderPathShadow(sm) + { + pRenderer = CG::me()->GetRenderer(); + + renderTarget = pRenderer->CreateRenderTarget(512, 512, FORMAT_R32_FLOAT); + + depthTarget = pRenderer->CreateDepthTarget(512, 512, FORMAT_D24_UNORM_S8_UINT); + cbCamera = pRenderer->CreateConstantBuffer(sizeof(PER_CAMERA_CONSTANTS)); + } + RenderPathSpotShadow::~RenderPathSpotShadow() + { + } + void RenderPathSpotShadow::Render(LifeGraphics::IScene* pScene) + { + Frustum f = *light.getFrustum(); + frustumQuery.SetFrustum(f); + frustumQuery.Filration = true; + frustumQuery.filter = NT_MESH; + //get all visible objects + pScene->PerformQuery(&frustumQuery); + + //if there is any visible objects for light frustum - start rendering shadow map + int objCount = frustumQuery.Size(); + if(objCount) + { + LifeMath::Rect2i vp(0,0,512,512); + pRenderer->SetViewport(vp); + pRenderer->SetScissorRectangle(vp); + + pRenderer->SetTargets(renderTarget, depthTarget); + LifeMath::float4 color = LifeMath::float4(1.0f, 1.0f, 1.0f, 1.0f); + pRenderer->ClearDepthStencil(depthTarget, 1.0f, 0); + + PER_CAMERA_CONSTANTS pccs = {0}; + + pccs.camNearFar = LifeMath::float4(0.1f, light.getRange(), 0.0f,0.0f); + pccs.mView = light.getView(); + pccs.mProj = light.getProj(); + pccs.mInvVP = pccs.mView * pccs.mProj; + LifeMath::float3 camPosl = light.getPosition(); + LifeMath::float3 camDirl = light.getDirection(); + pccs.camPos = camPosl; + + pccs.camDir = camDirl - camPosl; + LifeMath::Vec3Normalize(&pccs.camDir); + + cbCamera->Update(&pccs, sizeof(PER_CAMERA_CONSTANTS)); + pRenderer->SetVSConstantBuffer(cbCamera, 1); + pRenderer->SetPSConstantBuffer(cbCamera, 1); + + pRenderer->SetDepthStencilState(this->pShadowManager->depthState); + pRenderer->SetPSTextureSampler(this->pShadowManager->samplerPCF, 2); + + //render each visible object to shadow map + for(int i = 0; i < objCount; i++) + { + IObject* mesh = static_cast(frustumQuery[i]); + mesh->bind(); + + for(int i = 0; i < mesh->getSumbeshCount(); i++) + { + IMaterialProxy* mat = mesh->getMaterial(i); + mat->SelectTechnique(MT_RENDER_DEFERRED); + mat->GetTechnique()->SelectPass(MP_DEPTH_SM); + IMaterialPass* pass = mat->GetTechnique()->GetPass(); + pass->Apply(); + printf("Epic fail\n!"); + /*pRenderer->SetVertexShader(mat->g_buffer_pass_vs); + + if(mat->diffuseMap.ptr()) + { + pRenderer->SetPSTexture(mat->diffuseMap.ptr(), 0); + } + if(mat->AlphaTest || mat->AlphaBlendEnable) + { + pRenderer->SetPixelShader(pShadowManager->rtdPsAT); + } + else + { + pRenderer->SetPixelShader(pShadowManager->rtdPs); + }*/ + mesh->render(i); + } + } + + frustumQuery.Flush(); + } + } + void RenderPathSpotShadow::SetLight(const Light& _light) + { + light = _light; + } + } +} \ No newline at end of file diff --git a/LifeGraphics/renderpath/RenderPathSpotShadow.h b/LifeGraphics/renderpath/RenderPathSpotShadow.h new file mode 100644 index 0000000..4b5b812 --- /dev/null +++ b/LifeGraphics/renderpath/RenderPathSpotShadow.h @@ -0,0 +1,27 @@ +#ifndef RenderPathSpotShadow_h__ +#define RenderPathSpotShadow_h__ + +#include "renderpath/IRenderPathShadow.h" + +namespace LifeGraphics +{ + namespace RenderPath + { + class RenderPathSpotShadow : public IRenderPathShadow + { + public: + RenderPathSpotShadow(ShadowMapManager* sm); + ~RenderPathSpotShadow(); + void Render(LifeGraphics::IScene* pScene); + void SetLight(const Light& _light); + ITexture2D* GetShadowMap() + { + return renderTarget; + } + private: + ITexture2D* depthTarget; + ITexture2D* renderTarget; + }; + } +} +#endif // RenderPathSpotShadow_h__ \ No newline at end of file diff --git a/LifeGraphics/resource_ptr.h b/LifeGraphics/resource_ptr.h new file mode 100644 index 0000000..dba2701 --- /dev/null +++ b/LifeGraphics/resource_ptr.h @@ -0,0 +1,57 @@ +#ifndef resource_ptr_h__ +#define resource_ptr_h__ + +namespace LifeGraphics +{ + template + class resource_ptr + { + protected: + T* _ptr; + int _refCount; + public: + resource_ptr() + :_refCount(0), _ptr(0) + { + } + resource_ptr(T* ptr) + :_refCount(0), _ptr(0) + { + _ptr = ptr; + //touch(); + } + + ~resource_ptr(){} + T* ptr() {return _ptr;} + void resetPtr(T* ptr) + { + _refCount = 0; + _ptr = ptr; + // touch(); + } + + resource_ptr& operator=(const resource_ptr& ptr) { + //release(); + _ptr = ptr._ptr; + //_refCount = ptr._refCount; + //touch(); + return *this; + } + + T* operator->(){return _ptr;} + /* void touch() + { + _refCount++; + } + void release() + { + _refCount--; + }*/ + int refCount() + { + return _refCount; + } + }; +} + +#endif \ No newline at end of file diff --git a/LifeGraphics/scene/IScene.cpp b/LifeGraphics/scene/IScene.cpp new file mode 100644 index 0000000..e69de29 diff --git a/LifeGraphics/scene/ISceneManager.cpp b/LifeGraphics/scene/ISceneManager.cpp new file mode 100644 index 0000000..e69de29 diff --git a/LifeGraphics/shaders/IShaderFactorySource.cpp b/LifeGraphics/shaders/IShaderFactorySource.cpp new file mode 100644 index 0000000..7179e2a --- /dev/null +++ b/LifeGraphics/shaders/IShaderFactorySource.cpp @@ -0,0 +1,307 @@ +#include "shaders/IShaderFactorySource.h" +#include "shaders/IShaderGraphNodeFactory.h" +#include +#include + +namespace LifeGraphics +{ +#define NEW_NODE(_NodeName, _NodeType) \ + IShaderGraphNode* ##_NodeName## = new IShaderGraphNode(); \ + _NodeName##->Name = STR(#_NodeName); \ + _NodeName##->NodeType = ##_NodeType; + +#define NEW_NODE_IN(_NodeName, _DataType, _InName) \ + IShaderGraphNodePort* in_a##_InName##_DataType## = new IShaderGraphNodePort(##_NodeName##); \ + in_a##_InName##_DataType##->DataType = _DataType; \ + in_a##_InName##_DataType##->Name = STR(#_InName); \ + _NodeName##->Inputs.push_back(in_a##_InName##_DataType##); + +#define NEW_NODE_OUT(_NodeName, _DataType, _OutName) \ + IShaderGraphNodePort* out_a##_OutName##_DataType## = new IShaderGraphNodePort(##_NodeName##); \ + out_a##_OutName##_DataType##->DataType = _DataType; \ + out_a##_OutName##_DataType##->Name = STR(#_OutName); \ + _NodeName##->Outputs.push_back(out_a##_OutName##_DataType##); + + void IShaderFactorySource::Register(IShaderGraphNodeFactory* factory) + { + factory->RegisterNode(STR("Material"), &IShaderFactorySource::node_mat_std); + factory->RegisterNode(STR("OpAdd"), &IShaderFactorySource::node_op_add); + factory->RegisterNode(STR("OpLerp"), &IShaderFactorySource::node_op_lerp); + factory->RegisterNode(STR("OpSub"), &IShaderFactorySource::node_op_sub); + factory->RegisterNode(STR("OpMul"), &IShaderFactorySource::node_op_mul); + factory->RegisterNode(STR("OpDiv"), &IShaderFactorySource::node_op_div); + factory->RegisterNode(STR("OpDot"), &IShaderFactorySource::node_op_dot); + factory->RegisterNode(STR("ConstFloat1"), &IShaderFactorySource::node_const_f1); + factory->RegisterNode(STR("ConstFloat2"), &IShaderFactorySource::node_const_f2); + factory->RegisterNode(STR("ConstFloat3"), &IShaderFactorySource::node_const_f3); + factory->RegisterNode(STR("ConstFloat4"), &IShaderFactorySource::node_const_f4); + + factory->RegisterNode(STR("OpNormalize"), &IShaderFactorySource::node_op_normalize); + factory->RegisterNode(STR("SrcTex2D"), &IShaderFactorySource::node_src_sam_2d); + factory->RegisterNode(STR("SrcUV"), &IShaderFactorySource::node_src_uv); + + factory->RegisterNode(STR("DecodeNormal"), &IShaderFactorySource::node_op_decode_normal); + factory->RegisterNode(STR("Time"), &IShaderFactorySource::node_val_time); + } + IShaderGraphNode* IShaderFactorySource::node_mat_std() + { + NEW_NODE(Material, NT_MATERIAL); + NEW_NODE_IN(Material, DT_FLOAT3, Diffuse); + NEW_NODE_IN(Material, DT_FLOAT3, Specular); + NEW_NODE_IN(Material, DT_FLOAT, SpecularPower); + NEW_NODE_IN(Material, DT_FLOAT3, Normal); + return Material; + } + IShaderGraphNode* IShaderFactorySource::node_op_add() + { + NEW_NODE(Add, NT_OP); + NEW_NODE_IN(Add, DT_ANY, A); + NEW_NODE_IN(Add, DT_ANY, B); + NEW_NODE_OUT(Add, DT_ANY, C); + return Add; + } + + IShaderGraphNode* IShaderFactorySource::node_op_lerp() + { + NEW_NODE(Lerp, NT_OP); + NEW_NODE_IN(Lerp, DT_ANY, A); + NEW_NODE_IN(Lerp, DT_ANY, B); + NEW_NODE_IN(Lerp, DT_FLOAT, Factor); + NEW_NODE_OUT(Lerp, DT_ANY, C); + return Lerp; + } + + IShaderGraphNode* IShaderFactorySource::node_val_time() + { + NEW_NODE(Time, NT_CONST); + NEW_NODE_OUT(Time, DT_FLOAT4, RGBA); + NEW_NODE_OUT(Time, DT_FLOAT, R); + return Time; + } + + IShaderGraphNode* IShaderFactorySource::node_const_f1() + { + IShaderGraphNode* node = new IShaderGraphNode(); + node->Name = STR("ConstFloat1"); + node->NodeType = NT_CONST; + + IShaderGraphNodePort* out_a = new IShaderGraphNodePort(node); + out_a->DataType = DT_FLOAT; + out_a->Name = STR("A"); + + node->Outputs.push_back(out_a); + + LifeCore::Property* valueProperty = new LifeCore::Property(); + (*valueProperty) = 0.5f; + valueProperty->AddAttribute(STR("Name"), STR("Value")); + valueProperty->AddAttribute(STR("DisplayName"), STR("Value")); + node->RegisterProperty(valueProperty); + + return node; + } + IShaderGraphNode* IShaderFactorySource::node_const_f2() + { + IShaderGraphNode* node = new IShaderGraphNode(); + node->Name = STR("ConstFloat2"); + node->NodeType = NT_CONST; + + IShaderGraphNodePort* out_a = new IShaderGraphNodePort(node); + out_a->DataType = DT_FLOAT2; + out_a->Name = STR("A"); + + LifeCore::Property* valueProperty = new LifeCore::Property(); + (*valueProperty) = LifeMath::float2(0.0f, 0.0f); + valueProperty->AddAttribute(STR("Name"), STR("Value")); + valueProperty->AddAttribute(STR("DisplayName"), STR("Value")); + node->RegisterProperty(valueProperty); + + + node->Outputs.push_back(out_a); + return node; + } + IShaderGraphNode* IShaderFactorySource::node_const_f3() + { + IShaderGraphNode* node = new IShaderGraphNode(); + node->Name = STR("ConstFloat3"); + node->NodeType = NT_CONST; + + IShaderGraphNodePort* out_a = new IShaderGraphNodePort(node); + out_a->DataType = DT_FLOAT3; + out_a->Name = STR("A"); + + LifeCore::Property* valueProperty = new LifeCore::Property(); + (*valueProperty) = LifeMath::float3(0.0f, 0.0f, 0.0f); + valueProperty->AddAttribute(STR("Name"), STR("Value")); + valueProperty->AddAttribute(STR("DisplayName"), STR("Value")); + node->RegisterProperty(valueProperty); + + node->Outputs.push_back(out_a); + return node; + } + IShaderGraphNode* IShaderFactorySource::node_const_f4() + { + IShaderGraphNode* node = new IShaderGraphNode(); + node->Name = STR("ConstFloat4"); + node->NodeType = NT_CONST; + + IShaderGraphNodePort* out_a = new IShaderGraphNodePort(node); + out_a->DataType = DT_FLOAT4; + out_a->Name = STR("A"); + + LifeCore::Property* valueProperty = new LifeCore::Property(); + (*valueProperty) = LifeMath::float4(0.0f, 0.0f, 0.0f, 0.0f); + valueProperty->AddAttribute(STR("Name"), STR("Value")); + valueProperty->AddAttribute(STR("DisplayName"), STR("Value")); + node->RegisterProperty(valueProperty); + + + + node->Outputs.push_back(out_a); + return node; + } + IShaderGraphNode* IShaderFactorySource::node_op_sub() + { + return 0; + } + IShaderGraphNode* IShaderFactorySource::node_op_mul() + { + return 0; + } + IShaderGraphNode* IShaderFactorySource::node_op_div() + { + return 0; + } + IShaderGraphNode* IShaderFactorySource::node_op_dot() + { + return 0; + } + IShaderGraphNode* IShaderFactorySource::node_op_cross() + { + return 0; + } + IShaderGraphNode* IShaderFactorySource::node_op_normalize() + { + IShaderGraphNode* node = new IShaderGraphNode(); + node->Name = STR("Normalize"); + node->NodeType = NT_OP; + + + IShaderGraphNodePort* in_a = new IShaderGraphNodePort(node); + in_a->DataType = DT_ANY; + in_a->Name = STR("A"); + + IShaderGraphNodePort* out_a = new IShaderGraphNodePort(node); + out_a->DataType = DT_ANY; + out_a->Name = STR("B"); + + node->Inputs.push_back(in_a); + node->Outputs.push_back(out_a); + + return node; + } + IShaderGraphNode* IShaderFactorySource::node_op_saturate() + { + return 0; + } + IShaderGraphNode* IShaderFactorySource::node_op_cmp_le() + { + return 0; + } + IShaderGraphNode* IShaderFactorySource::node_op_cmp_l() + { + return 0; + } + IShaderGraphNode* IShaderFactorySource::node_op_cmp_ge() + { + return 0; + } + IShaderGraphNode* IShaderFactorySource::node_op_cmp_g() + { + return 0; + } + IShaderGraphNode* IShaderFactorySource::node_op_cmp_e() + { + return 0; + } + + IShaderGraphNode* IShaderFactorySource::node_op_decode_g() + { + return 0; + } + IShaderGraphNode* IShaderFactorySource::node_op_decode_normal() + { + NEW_NODE(DecodeNormal, NT_OP); + NEW_NODE_IN(DecodeNormal, DT_ANY, TexNormal); + NEW_NODE_OUT(DecodeNormal, DT_FLOAT3, Normal); + return DecodeNormal; + } + + IShaderGraphNode* IShaderFactorySource::node_src_uv() + { + NEW_NODE(UV, NT_RES); + NEW_NODE_OUT(UV, DT_FLOAT2, UV); + + LifeCore::Property* channelProperty = new LifeCore::Property(); + (*channelProperty) = 0; + channelProperty->AddAttribute(STR("Name"), STR("Channel")); + channelProperty->AddAttribute(STR("DisplayName"), STR("UV Channel ID")); + UV->RegisterProperty(channelProperty); + + return UV; + } + IShaderGraphNode* IShaderFactorySource::node_src_sam_2d() + { + IShaderGraphNode* node = new IShaderGraphNode(); + node->Name = STR("Sam2D"); + node->NodeType = NT_RES; + + IShaderGraphNodePort* in_a = new IShaderGraphNodePort(node); + in_a->DataType = DT_FLOAT2; + in_a->Name = STR("UV"); + + IShaderGraphNodePort* out_a = new IShaderGraphNodePort(node); + out_a->DataType = DT_FLOAT4; + out_a->Name = STR("RGBA"); + + IShaderGraphNodePort* out_b = new IShaderGraphNodePort(node); + out_b->DataType = DT_FLOAT; + out_b->Name = STR("R"); + + IShaderGraphNodePort* out_c = new IShaderGraphNodePort(node); + out_c->DataType = DT_FLOAT; + out_c->Name = STR("G"); + + IShaderGraphNodePort* out_d = new IShaderGraphNodePort(node); + out_d->DataType = DT_FLOAT; + out_d->Name = STR("B"); + + IShaderGraphNodePort* out_e = new IShaderGraphNodePort(node); + out_e->DataType = DT_FLOAT; + out_e->Name = STR("A"); + + node->Inputs.push_back(in_a); + node->Outputs.push_back(out_a); + node->Outputs.push_back(out_b); + node->Outputs.push_back(out_c); + node->Outputs.push_back(out_d); + node->Outputs.push_back(out_e); + + + LifeCore::Property* filename = new LifeCore::Property(); + (*filename) = STR(""); + filename->AddAttribute(STR("Name"), STR("FilePath")); + filename->AddAttribute(STR("DisplayName"), STR("FilePath")); + filename->AddAttribute(STR("ValueEditor"), STR("FilePath")); + node->RegisterProperty(filename); + + return node; + } + IShaderGraphNode* IShaderFactorySource::node_src_sam_3d() + { + return 0; + } + IShaderGraphNode* IShaderFactorySource::node_src_sam_cube() + { + return 0; + } +} \ No newline at end of file diff --git a/LifeGraphics/shaders/IShaderFactorySource.h b/LifeGraphics/shaders/IShaderFactorySource.h new file mode 100644 index 0000000..588a2f6 --- /dev/null +++ b/LifeGraphics/shaders/IShaderFactorySource.h @@ -0,0 +1,45 @@ +#ifndef IShaderFactorySource_h__ +#define IShaderFactorySource_h__ + +#include "shaders/IShaderGraphNode.h" + +namespace LifeGraphics +{ + class IShaderGraphNodeFactory; + class IShaderFactorySource + { + public: + static void Register(IShaderGraphNodeFactory* factory); + static IShaderGraphNode* node_mat_std(); + static IShaderGraphNode* node_op_add(); + static IShaderGraphNode* node_op_lerp(); + static IShaderGraphNode* node_op_sub(); + static IShaderGraphNode* node_op_mul(); + static IShaderGraphNode* node_op_div(); + static IShaderGraphNode* node_op_dot(); + static IShaderGraphNode* node_op_cross(); + static IShaderGraphNode* node_op_normalize(); + static IShaderGraphNode* node_op_saturate(); + static IShaderGraphNode* node_op_cmp_le(); + static IShaderGraphNode* node_op_cmp_l(); + static IShaderGraphNode* node_op_cmp_ge(); + static IShaderGraphNode* node_op_cmp_g(); + static IShaderGraphNode* node_op_cmp_e(); + + static IShaderGraphNode* node_op_decode_g(); + static IShaderGraphNode* node_op_decode_normal(); + + static IShaderGraphNode* node_src_uv(); + static IShaderGraphNode* node_src_sam_2d(); + static IShaderGraphNode* node_src_sam_3d(); + static IShaderGraphNode* node_src_sam_cube(); + + static IShaderGraphNode* node_const_f1(); + static IShaderGraphNode* node_const_f2(); + static IShaderGraphNode* node_const_f3(); + static IShaderGraphNode* node_const_f4(); + + static IShaderGraphNode* node_val_time(); + }; +} +#endif // IShaderFactorySource_h__ \ No newline at end of file diff --git a/LifeGraphics/shaders/IShaderGraph.cpp b/LifeGraphics/shaders/IShaderGraph.cpp new file mode 100644 index 0000000..651c9b6 --- /dev/null +++ b/LifeGraphics/shaders/IShaderGraph.cpp @@ -0,0 +1,207 @@ +#include "shaders/IShaderGraph.h" +#include +#include "XML.h" + +#define NAME_LENGTH 64 + +namespace LifeGraphics +{ + IShaderGraph::IShaderGraph(IShaderGraphNodeFactory* factory) + :last_id(0) + { + pFactory = factory; + for(int i = 0; i < 16; i++) + { + texture_slots[i] = -1; + } + } + IShaderGraph::~IShaderGraph() + { + } + int IShaderGraph::GrabTextureSlot() + { + for(int i = 0; i < 16; i++) + { + if(texture_slots[i] == -1) + { + texture_slots[i] = 1; + return i; + } + } + return -1; + } + IShaderGraphNode* IShaderGraph::AddNode(const String& name) + { + IShaderGraphNode* node = pFactory->Build(name); + node->id = GetID(); + Nodes.push_back(node); + + if(name == STR("SrcTex2D")) + { + node->resource_id = GrabTextureSlot(); + } + return node; + } + bool IShaderGraph::DeleteNode(int id) + { + for(NodeCollectionIt it = Nodes.begin(); it != Nodes.end(); ++it) + { + IShaderGraphNode* n = (*it); + if(n->id == id) { + + //deactivate output links + for(int j = 0; j < n->Outputs.size(); j++){ + IShaderGraphNodePort* port = n->Outputs[j]; + port->BreakLink(); + delete port; + } + + //deactivate input links + for(int j = 0; j < n->Inputs.size(); j++){ + IShaderGraphNodePort* port = n->Inputs[j]; + port->BreakLink(); + delete port; + } + + + delete (*it); + Nodes.erase(it); + return true; + } + } + return false; + } + IShaderGraphNode* IShaderGraph::GetNodeByID(int id) + { + int nc = Nodes.size(); + for(int i = 0; i < nc; i++) + { + if(Nodes[i]->id == id) + return Nodes[i]; + } + return 0; + } + void IShaderGraph::Clear() + { + int nc = Nodes.size(); + for(int i = 0; i < nc; i++) + { + delete Nodes[i]; + } + Nodes.clear(); + } + IShaderGraphNode* IShaderGraph::GetNodeByArrayID(int id) + { + int nc = Nodes.size(); + if(id < nc) + { + return Nodes[id]; + } + return nullptr; + } + + size_t IShaderGraph::GetNodeCount() + { + return Nodes.size(); + } + void IShaderGraph::SerializeXML(LifeCore::IMemoryStream* stm) + { + /*int node_count = Nodes.size(); + pugi::xml_document doc; + pugi::xml_node rootNode = doc.append_child("ShaderGraph"); + rootNode.append_attribute("NodeCount") = node_count; + rootNode.append_attribute("LastID") = last_id; + + //write node info + for(int i = 0; i < node_count; i++) + { + IShaderGraphNode* n = Nodes[i]; + pugi::xml_node node = rootNode.append_child("Node"); + node.append_attribute("Name") = n->Name; + node.append_attribute("ID") = n->id; + + }*/ + } + bool IShaderGraph::DeserializeXML(LifeCore::IMemoryStream* stm) + { + + return true; + } + void IShaderGraph::Serialize(LifeCore::IMemoryStream* stm) + { + + int node_count = Nodes.size(); + //write total nodes count + stm->Write(node_count); + //write last id value + stm->Write(last_id); + + //write basic node info + for(int i = 0; i < node_count; i++){ + IShaderGraphNode* n = Nodes[i]; + StringSerialize(n->Name, stm); + stm->Write(n->id); + } + + //write advanced node info + for(int i = 0; i < node_count; i++){ + IShaderGraphNode* n = Nodes[i]; + n->Serialize(stm); //serialize properties + for(int j = 0; j < n->Inputs.size(); j++){ + IShaderGraphNodePort* port = n->Inputs[j]; + int val = -1; + int parent = -1; + if(port->GetLinkCount()){ + val = port->GetLink(0)->Parent->GetOutputID(port->GetLink(0)); + parent = port->GetLink(0)->Parent->id; + } + //write attached node ID + stm->Write(parent); + //write attached node port ID + stm->Write(val); + } + } + } + + bool IShaderGraph::Deserialize(LifeCore::IMemoryStream* stm) + { + int node_count; + stm->ReadInt32(node_count); + stm->ReadInt32(last_id); + + //read basic info and create nodes + for(int i = 0; i < node_count; i++){ + //get node name + String name = StringDeserialize(stm); + IShaderGraphNode* n = pFactory->Build(name); + //get node ID + stm->ReadInt32(n->id); + Nodes.push_back(n); + } + + //read extended info + for(int i = 0; i < node_count; i++){ + IShaderGraphNode* n = Nodes[i]; + n->Deserialize(stm); //deserialize properties + int pc = n->Inputs.size(); + + //read ports info + for(int j = 0; j < pc; j++){ + int nid, pid; + //read attached node id + stm->ReadInt32(nid); + //read attached port id + stm->ReadInt32(pid); + //if attached - make link + if(pid != -1){ + //current node + IShaderGraphNode* cn = Nodes[i]; + //attachment node + IShaderGraphNode* an = GetNodeByID(nid); + cn->Inputs[j]->MakeLink(cn->Inputs[j], an->Outputs[pid]); + } + } + } + return true; + } +} \ No newline at end of file diff --git a/LifeGraphics/shaders/IShaderGraph.h b/LifeGraphics/shaders/IShaderGraph.h new file mode 100644 index 0000000..76b9f54 --- /dev/null +++ b/LifeGraphics/shaders/IShaderGraph.h @@ -0,0 +1,40 @@ +#ifndef IShaderGraph_h__ +#define IShaderGraph_h__ + +#include "shaders/IShaderGraphNodeFactory.h" +#include "delegate/Delegate.h" +#include +#include + +namespace LifeGraphics +{ + class IShaderGraph + { + public: + EVENT1(GraphEvent, IShaderGraphNode*); + GraphEvent Event_AddNode; + + IShaderGraph(IShaderGraphNodeFactory* factory); + ~IShaderGraph(); + IShaderGraphNode* AddNode(const String& name); + bool DeleteNode(int id); + void Serialize(LifeCore::IMemoryStream* stm); + bool Deserialize(LifeCore::IMemoryStream* stm); + void SerializeXML(LifeCore::IMemoryStream* stm); + bool DeserializeXML(LifeCore::IMemoryStream* stm); + IShaderGraphNode* GetNodeByID(int id); + IShaderGraphNode* GetNodeByArrayID(int id); + size_t GetNodeCount(); + void Clear(); + private: + int texture_slots[16]; + int last_id; + int GrabTextureSlot(); + int GetID(){return last_id++;} + IShaderGraphNodeFactory* pFactory; + typedef std::vector NodeCollection; + typedef std::vector::iterator NodeCollectionIt; + NodeCollection Nodes; + }; +} +#endif // IShaderGraph_h__ \ No newline at end of file diff --git a/LifeGraphics/shaders/IShaderGraphCompiler.h b/LifeGraphics/shaders/IShaderGraphCompiler.h new file mode 100644 index 0000000..518da9e --- /dev/null +++ b/LifeGraphics/shaders/IShaderGraphCompiler.h @@ -0,0 +1,70 @@ +#ifndef IShaderGraphCompiler_h__ +#define IShaderGraphCompiler_h__ + +#include "shaders/IShaderGraphNode.h" + +namespace LifeGraphics +{ + enum IShaderCompilerFlags + { + SCF_NONE = 0, + SCF_SKINNING = 1, + }; + + static int ShaderTypeToChannelCount(IShaderDataType t) + { + switch(t) + { + case DT_ANY: + return 0; + case DT_FLOAT: + return 1; + case DT_FLOAT2: + return 2; + case DT_FLOAT3: + return 3; + case DT_FLOAT4: + return 4; + } + return 0; + } + + static IShaderDataType MaxType(IShaderDataType t1, IShaderDataType t2) + { + return ShaderTypeToChannelCount(t1) > ShaderTypeToChannelCount(t2) ? t1 : t2; + } + + struct ShaderTextureMapping + { + int reg; + int nodeId; + String name; + }; + struct ShaderConstantMapping + { + String name; + IShaderDataType DataType; + int size; + int offset; + }; + struct ShaderConstantTable + { + + }; + + struct ShaderInputInfo + { + std::vector Textures; + void Clear() + { + Textures.clear(); + } + }; + + class IShaderGraphCompiler + { + public: + virtual void CompileNode(IShaderGraphNode* rootNode, int flags, String& ps, String& vs, ShaderInputInfo* info) = 0; + }; +} +#endif // IShaderGraphCompiler_h__ \ No newline at end of file diff --git a/LifeGraphics/shaders/IShaderGraphNode.cpp b/LifeGraphics/shaders/IShaderGraphNode.cpp new file mode 100644 index 0000000..8073b50 --- /dev/null +++ b/LifeGraphics/shaders/IShaderGraphNode.cpp @@ -0,0 +1,59 @@ +#include + + +namespace LifeGraphics +{ + IShaderGraphNode::IShaderGraphNode() + :checked(false), resource_id(0) + { + } + + bool IShaderGraphNode::Serialize(LifeCore::IMemoryStream* stm) + { + return IPropertyHolder::Serialize(stm); + } + bool IShaderGraphNode::Deserialize(LifeCore::IMemoryStream* stm) + { + return IPropertyHolder::Deserialize(stm); + } + IShaderGraphNodePort* IShaderGraphNode::GetInputPort(size_t id) + { + return Inputs[id]; + } + IShaderGraphNodePort* IShaderGraphNode::GetInputPortByName(const String& name) + { + size_t cnt = Inputs.size(); + + for(size_t i = 0; i < cnt; i++){ + if(Inputs[i]->Name == name) + return Inputs[i]; + } + return nullptr; + } + + IShaderGraphNodePort* IShaderGraphNode::GetOutputPortByName(const String& name) + { + size_t cnt = Outputs.size(); + + for(size_t i = 0; i < cnt; i++){ + if(Outputs[i]->Name == name) + return Outputs[i]; + } + return nullptr; + } + IShaderGraphNodePort* IShaderGraphNode::GetOutputPortByID(size_t id){ + size_t cnt = Outputs.size(); + if(id < cnt){ + return Outputs[id]; + } + return nullptr; + } + size_t IShaderGraphNode::GetOutputID(IShaderGraphNodePort* port) + { + for(size_t i = 0; i < Outputs.size(); i++){ + if(Outputs[i] == port) + return i; + } + return 100500; + } +} diff --git a/LifeGraphics/shaders/IShaderGraphNode.h b/LifeGraphics/shaders/IShaderGraphNode.h new file mode 100644 index 0000000..4635bbc --- /dev/null +++ b/LifeGraphics/shaders/IShaderGraphNode.h @@ -0,0 +1,36 @@ +#ifndef IShaderGraphNode_h__ +#define IShaderGraphNode_h__ + +#include +#include "IShaderTypes.h" +#include +#include "IPropertyHolder.h" +#include +#include "shaders/IShaderGraphNodePort.h" + +namespace LifeGraphics +{ + class IShaderGraphNode : public LifeCore::IPropertyHolder + { + public: + int resource_id; + ShaderNodeType NodeType; + String Name; + bool checked; + int id; + typedef std::vector PortCollection; + typedef std::vector::iterator PortCollectionItr; + PortCollection Inputs; + PortCollection Outputs; + IShaderGraphNode(); + IShaderGraphNodePort* GetInputPortByName(const String& name); + IShaderGraphNodePort* GetInputPort(size_t id); + IShaderGraphNodePort* GetOutputPortByName(const String& name); + IShaderGraphNodePort* GetOutputPortByID(size_t id); + bool Serialize(LifeCore::IMemoryStream* stm); + bool Deserialize(LifeCore::IMemoryStream* stm); + size_t GetOutputID(IShaderGraphNodePort* port); + }; +} + +#endif // IShaderGraphNode_h__ \ No newline at end of file diff --git a/LifeGraphics/shaders/IShaderGraphNodeFactory.cpp b/LifeGraphics/shaders/IShaderGraphNodeFactory.cpp new file mode 100644 index 0000000..7842a3a --- /dev/null +++ b/LifeGraphics/shaders/IShaderGraphNodeFactory.cpp @@ -0,0 +1,19 @@ +#include "shaders/IShaderGraphNodeFactory.h" + +namespace LifeGraphics +{ + void IShaderGraphNodeFactory::RegisterNode(const String& name, NodeFactoryFunc func) + { + if(FactoryFuncs.find(name) != FactoryFuncs.end()) + return; + FactoryFuncs[name] = func; + } + IShaderGraphNode* IShaderGraphNodeFactory::Build(const String& name) + { + if(FactoryFuncs.find(name) == FactoryFuncs.end()) + return 0; + IShaderGraphNode* n = FactoryFuncs[name](); + n->Name = name; + return n; + } +} \ No newline at end of file diff --git a/LifeGraphics/shaders/IShaderGraphNodeFactory.h b/LifeGraphics/shaders/IShaderGraphNodeFactory.h new file mode 100644 index 0000000..af82930 --- /dev/null +++ b/LifeGraphics/shaders/IShaderGraphNodeFactory.h @@ -0,0 +1,25 @@ +#ifndef IShaderGraphNodeFactory_h__ +#define IShaderGraphNodeFactory_h__ + +#include "shaders/IShaderGraphNode.h" +#include +#include + +namespace LifeGraphics +{ + class IShaderGraphNodeFactory; + class IShaderGraphNodeFactory : public Singleton + { + public: + friend class Singleton; + typedef IShaderGraphNode* (*NodeFactoryFunc)(); + void RegisterNode(const String& name, NodeFactoryFunc func); + IShaderGraphNode* Build(const String& name); + private: + IShaderGraphNodeFactory(){} + typedef std::hash_map FactoryArray; + FactoryArray FactoryFuncs; + }; +} + +#endif // IShaderGraphNodeFactory_h__ \ No newline at end of file diff --git a/LifeGraphics/shaders/IShaderGraphNodePort.cpp b/LifeGraphics/shaders/IShaderGraphNodePort.cpp new file mode 100644 index 0000000..4491e89 --- /dev/null +++ b/LifeGraphics/shaders/IShaderGraphNodePort.cpp @@ -0,0 +1,58 @@ +#include "shaders/IShaderGraphNodePort.h" +#include + +namespace LifeGraphics +{ + IShaderGraphNodePort::IShaderGraphNodePort(IShaderGraphNode* parent) + :Parent(parent) + { + assert(parent != nullptr && "Shader graph node port parent is nullptr!"); + } + + IShaderGraphNodePort* IShaderGraphNodePort::GetLink(size_t id) + { + return links[id]; + } + size_t IShaderGraphNodePort::GetLinkCount() + { + return links.size(); + } + void IShaderGraphNodePort::MakeLink(IShaderGraphNodePort* input, IShaderGraphNodePort* output) + { + // assert(target != nullptr && "Failed on add link on IShaderGraphNodePort"); + if(!input->IsLinkExists(output)) + input->links.push_back(output); + + if(!output->IsLinkExists(input)) + output->links.push_back(input); + } + bool IShaderGraphNodePort::IsLinkExists(IShaderGraphNodePort* target) + { + for(LinkCollectonItr it = links.begin(); it != links.end(); ++it) + { + if(*it == target) + return true; + } + return false; + } + void IShaderGraphNodePort::BreakLink() + { + //remove self from remote ports + for(LinkCollectonItr it = links.begin(); it != links.end(); ++it) { + (*it)->RemoveLink(this); + } + //clear list of remote ports + links.clear(); + } + void IShaderGraphNodePort::RemoveLink(IShaderGraphNodePort* target) + { + for(LinkCollectonItr it = links.begin(); it != links.end(); ++it) + { + IShaderGraphNodePort* lnk = *it; + if(lnk == target){ + links.erase(it); + return; + } + } + } +} \ No newline at end of file diff --git a/LifeGraphics/shaders/IShaderGraphNodePort.h b/LifeGraphics/shaders/IShaderGraphNodePort.h new file mode 100644 index 0000000..d421a28 --- /dev/null +++ b/LifeGraphics/shaders/IShaderGraphNodePort.h @@ -0,0 +1,30 @@ +#ifndef IShaderGraphNodePort_h__ +#define IShaderGraphNodePort_h__ + +#include "IShaderTypes.h" +#include + +namespace LifeGraphics +{ + class IShaderGraphNode; + class IShaderGraphNodePort + { + public: + void MakeLink(IShaderGraphNodePort* input, IShaderGraphNodePort* output); + IShaderGraphNodePort* GetLink(size_t id); + size_t GetLinkCount(); + bool IsLinkExists(IShaderGraphNodePort* target); + void BreakLink(); + IShaderGraphNodePort(IShaderGraphNode* parent); + IShaderGraphNode* Parent; + String Name; + String Value; + IShaderDataType DataType; + private: + void RemoveLink(IShaderGraphNodePort* target); + typedef std::vector LinkCollecton; + typedef std::vector::iterator LinkCollectonItr; + LinkCollecton links; + }; +} +#endif // IShaderGraphNodePort_h__ diff --git a/LifeGraphics/shaders/IShaderTypes.h b/LifeGraphics/shaders/IShaderTypes.h new file mode 100644 index 0000000..49b594f --- /dev/null +++ b/LifeGraphics/shaders/IShaderTypes.h @@ -0,0 +1,22 @@ +#ifndef IShaderTypes_h__ +#define IShaderTypes_h__ + +#include + +enum IShaderDataType +{ + DT_ANY, + DT_FLOAT, + DT_FLOAT2, + DT_FLOAT3, + DT_FLOAT4, +}; + +enum ShaderNodeType +{ + NT_CONST, + NT_OP, + NT_RES, + NT_MATERIAL +}; +#endif // IShaderTypes_h__ diff --git a/LifeGraphics/shaders/ShaderGraphCompilerHLSL.cpp b/LifeGraphics/shaders/ShaderGraphCompilerHLSL.cpp new file mode 100644 index 0000000..9a4f18f --- /dev/null +++ b/LifeGraphics/shaders/ShaderGraphCompilerHLSL.cpp @@ -0,0 +1,520 @@ +#include "shaders/ShaderGraphCompilerHLSL.h" +#include +#include +#include +#include + +namespace LifeGraphics +{ + ShaderGraphCompilerHLSL::ShaderGraphCompilerHLSL() + :var_id(0) + { + } + String ShaderGraphCompilerHLSL::NewVarName(const String& baseName) + { + char tmp[32]; + itoa(var_id, &tmp[0], 10); + var_id++; + return baseName + String(tmp); + } + String ShaderGraphCompilerHLSL::TypeToStr(IShaderDataType t) + { + switch(t) + { + case DT_FLOAT: + return STR("float"); + case DT_FLOAT2: + return STR("float2"); + case DT_FLOAT3: + return STR("float3"); + case DT_FLOAT4: + return STR("float4"); + default: + return STR("ERROR_TYPE"); + } + } + void ShaderGraphCompilerHLSL::ExposeNode(String& shader, String& resources, IShaderGraphNode* node) + { + if(node->checked) + return; + + node->checked = true; + //TCHAR* tmp = new TCHAR[128]; + TCHAR tmp[1024]; + //constant values + if(node->NodeType == NT_CONST) + { + if(node->Name == STR("ConstFloat1")) + { + String constName = NewVarName(node->Name); + node->Outputs[0]->Value = constName; + + LifeCore::IProperty* valueProp; + if(!node->GetPropertyByAttribute(STR("Name"), STR("Value"), &valueProp)) + { + printf("Failed to get \"Value\" Property from Float1 node!\n"); + getchar(); + exit(0); + } + + String s = valueProp->ToString(); + sprintf(&tmp[0], "float %s = %sf;\n", constName.c_str(), s.c_str()); + shader = String(tmp) + shader; + } + else if(node->Name == STR("ConstFloat2")) + { + String constName = NewVarName(node->Name); + node->Outputs[0]->Value = constName; + + LifeCore::IProperty* valueProp; + if(!node->GetPropertyByAttribute(STR("Name"), STR("Value"), &valueProp)) + { + printf("Failed to get \"Value\" Property from Float1 node!\n"); + getchar(); + exit(0); + } + + LifeCore::Property* prop_src = (LifeCore::Property*)valueProp; + + sprintf(&tmp[0], "float2 %s = float2(%ff, %ff);\n", constName.c_str(), (*prop_src)().X, (*prop_src)().Y); + shader = String(tmp) + shader; + } + else if(node->Name == STR("ConstFloat3")) + { + String constName = NewVarName(node->Name); + node->Outputs[0]->Value = constName; + + LifeCore::IProperty* valueProp; + if(!node->GetPropertyByAttribute(STR("Name"), STR("Value"), &valueProp)) + { + printf("Failed to get \"Value\" Property from Float1 node!\n"); + getchar(); + exit(0); + } + LifeCore::Property* prop_src = (LifeCore::Property*)valueProp; + + + sprintf(&tmp[0], "float3 %s = float3(%ff, %ff, %ff);\n", constName.c_str(), (*prop_src)().X, (*prop_src)().Y, (*prop_src)().Z); + shader = String(tmp) + shader; + } + else if(node->Name == STR("ConstFloat4")) + { + String constName = NewVarName(node->Name); + node->Outputs[0]->Value = constName; + + LifeCore::IProperty* valueProp; + if(!node->GetPropertyByAttribute(STR("Name"), STR("Value"), &valueProp)) + { + printf("Failed to get \"Value\" Property from Float1 node!\n"); + getchar(); + exit(0); + } + LifeCore::Property* prop_src = (LifeCore::Property*)valueProp; + + + sprintf(&tmp[0], "float4 %s = float4(%ff, %ff, %ff, %ff);\n", constName.c_str(), (*prop_src)().X, (*prop_src)().Y, (*prop_src)().Z, (*prop_src)().W); + shader = String(tmp) + shader; + } + else if(node->Name == STR("Time")) + { + String constName = NewVarName(node->Name); + node->Outputs[0]->Value = STR("sceneTime.xxxx"); + node->Outputs[1]->Value = STR("sceneTime.x"); + } + } + else + { + if(node->Name == STR("OpAdd")) + { + if(!node->Inputs[0]->GetLinkCount() || !node->Inputs[1]->GetLinkCount()){ + shader = String("ERROR_EXPRESSION") + shader; return;} + + String top1; + String top2; + //Exposing connected nodes + ExposeNode(top1, resources, node->Inputs[0]->GetLink(0)->Parent); + ExposeNode(top2, resources, node->Inputs[1]->GetLink(0)->Parent); + + String tn = TypeToStr(MaxType(node->Inputs[0]->GetLink(0)->DataType, node->Inputs[1]->GetLink(0)->DataType)); + String cn = NewVarName(node->Name); + sprintf(&tmp[0], "%s %s = ((%s) + (%s));", tn.c_str(), cn.c_str(), node->Inputs[0]->GetLink(0)->Value.c_str(), + node->Inputs[1]->GetLink(0)->Value.c_str()); + node->Outputs[0]->DataType = MaxType(node->Inputs[0]->GetLink(0)->DataType, node->Inputs[1]->GetLink(0)->DataType); + shader = top1 + top2 + String(tmp) + shader; + + node->Outputs[0]->Value = cn; + } + else if(node->Name == STR("OpLerp")) + { + if(!node->Inputs[0]->GetLinkCount() || !node->Inputs[1]->GetLinkCount() || !node->Inputs[2]->GetLinkCount()){ + shader = String("ERROR_EXPRESSION") + shader; return;} + + String top1; + String top2; + String top3; + //Exposing connected nodes + ExposeNode(top1, resources, node->Inputs[0]->GetLink(0)->Parent); + ExposeNode(top2, resources, node->Inputs[1]->GetLink(0)->Parent); + ExposeNode(top3, resources, node->Inputs[2]->GetLink(0)->Parent); + + String tn = TypeToStr(MaxType(node->Inputs[0]->GetLink(0)->DataType, node->Inputs[1]->GetLink(0)->DataType)); + String cn = NewVarName(node->Name); + sprintf(&tmp[0], "%s %s = lerp((%s),(%s),(%s));", tn.c_str(), cn.c_str(), node->Inputs[0]->GetLink(0)->Value.c_str(), + node->Inputs[1]->GetLink(0)->Value.c_str(), node->Inputs[2]->GetLink(0)->Value.c_str()); + node->Outputs[0]->DataType = MaxType(node->Inputs[0]->GetLink(0)->DataType, node->Inputs[1]->GetLink(0)->DataType); + shader = top1 + top2 + top3 + String(tmp) + shader; + + node->Outputs[0]->Value = cn; + } + else if(node->Name == STR("SrcTex2D")) + { + //check UV input slot + if(!node->Inputs[0]->GetLinkCount()){ + shader = String("ERROR_EXPRESSION") + shader; return;} + + String top1; + ExposeNode(top1, resources, node->Inputs[0]->GetLink(0)->Parent); + //String tn = TypeToStr(node->Inputs[0]->Link->DataType); + String cn = NewVarName(node->Name); + node->resource_id = lastTexSlot; + + //generate header's texture definition + sprintf(&tmp[0], "Texture2D SrcTexture2D%i : register(t%i);\n", lastTexSlot, lastTexSlot); + //write texture definition to the shader's header + ShaderTextureMapping mapping; + mapping.reg = node->resource_id; + mapping.name = String("SrcTexture2D") + Int32ToString(node->resource_id); + mapping.nodeId = node->id; + inputInfo.Textures.push_back(mapping); + + resources += String(tmp); + + sprintf(&tmp[0], "float4 %s = SrcTexture2D%i.Sample(linearSampler, %s);\n", cn.c_str(), node->resource_id, node->Inputs[0]->GetLink(0)->Value.c_str()); + shader = top1 + String(tmp) + shader; + + node->Outputs[0]->Value = cn + STR(".xyzw"); + node->Outputs[1]->Value = cn + STR(".xxxx"); + node->Outputs[2]->Value = cn + STR(".yyyy"); + node->Outputs[3]->Value = cn + STR(".zzzz"); + node->Outputs[4]->Value = cn + STR(".wwww"); + + lastTexSlot++; + } + else if(node->Name == STR("SrcUV")) + { + LifeCore::IProperty* channelProp; + if(!node->GetPropertyByAttribute(STR("Name"), STR("Channel"), &channelProp)) + { + printf("Failed to get \"Channel\" Property from UV node!\n"); + getchar(); + exit(0); + } + + node->Outputs[0]->Value = STR("Input.Texcoord") + channelProp->ToString() + STR(".xy"); + } + else if(node->Name == STR("DecodeNormal")) + { + String top1; + ExposeNode(top1, resources, node->Inputs[0]->GetLink(0)->Parent); + + String cn = NewVarName(node->Name); + sprintf(&tmp[0], "float3 %s = normalize(((%s).xyz * 2.0f) - 1.0f);", cn.c_str(), node->Inputs[0]->GetLink(0)->Value.c_str()); + String src1(tmp); + + sprintf(&tmp[0], "%s = normalize((%s).x * normalize(Input.Tangent) + (%s).y * normalize(Input.Binormal) + (%s).z * normalize(Input.Normal.xyz));", cn.c_str(), cn.c_str(),cn.c_str(),cn.c_str()); + String src2(tmp); + + sprintf(&tmp[0], "%s = (%s).xyz * 0.5f + 0.5f;", cn.c_str(), cn.c_str()); + + String src3(tmp); + + shader = top1 + src1 + src2 + src3 + shader; + + node->Outputs[0]->Value = cn; + /* + float3 n = normalize(nn.x * normalize(Input.Tangent) + nn.y * normalize(Input.Binormal) + nn.z * normalize(Input.Normal.xyz)); + */ + } + else + { + shader = String("ERROR_EXPRESSION") + shader; + return; + } + } + // delete[] tmp; + } + void ShaderGraphCompilerHLSL::RemoveChecked(IShaderGraphNode* node) + { + node->checked = false; + for(IShaderGraphNode::PortCollectionItr it = node->Inputs.begin(); it != node->Inputs.end(); ++it) + { + IShaderGraphNodePort* port = *it; //my input port + + + if(port->GetLinkCount()) + { + RemoveChecked(port->GetLink(0)->Parent); + } + } + } + void ShaderGraphCompilerHLSL::CompileNode(IShaderGraphNode* rootNode, int flags, String& ps, String& vs, ShaderInputInfo* info) + { + //remove "checked" flag from all nodes + lastTexSlot = 0; + + inputInfo.Clear(); + RemoveChecked(rootNode); + + String per_object_cb = "cbuffer perObjectData : register(b0){row_major matrix mWorld;};\n"; + String per_camera_cb = + "cbuffer perCameraData : register(b1)\n" \ + "{\n" \ + " row_major matrix matView;\n" \ + " row_major matrix matProjection;\n" \ + " row_major matrix matVP;\n" \ + " float4 camPos;\n" \ + " float4 camDir;\n" \ + " float4 camNearFarVPWH;\n" \ + " float4 corners[4];\n" \ + "};\n"; + + /*per_camera_cb += + "cbuffer materialData : register(b2){\n" \ + "float shininess : packoffset(c0.x);\n" \ + "float specularPower : packoffset(c0.y);\n" \ + "float2 materialDummy : packoffset(c0.z);};\n";*/ + + per_camera_cb += + "cbuffer sceneData : register(b3){\n" \ + "float sceneTime : packoffset(c0.x);\n" \ + "float3 sceneAmbient : packoffset(c0.y);};\n"; + + String shader; + String resources; + + resources += + "SamplerState linearSampler : register(s0);\n" \ + "SamplerState pointSampler : register(s1);\n"; + + if(rootNode->Name != STR("Material")) + return; + + int inSlotCount = rootNode->Inputs.size(); + + String diffuse_code; + String normal_code; + String specular_code; + String specular_power_code; + + for(int i = 0; i < inSlotCount; i++) + { + String shader_tmp; + IShaderGraphNodePort* port = rootNode->Inputs[i]; + + if(port->Name == STR("Diffuse")) + { + if(port->GetLinkCount()) + { + ExposeNode(shader_tmp, resources, port->GetLink(0)->Parent); + diffuse_code = port->GetLink(0)->Value; + } + } + else if(port->Name == STR("Normal")) + { + if(port->GetLinkCount()) + { + ExposeNode(shader_tmp, resources, port->GetLink(0)->Parent); + normal_code = port->GetLink(0)->Value; + } + else + { + normal_code = "(Input.Normal * 0.5f + 0.5f)"; + } + } + else if(port->Name == STR("Specular")) + { + if(port->GetLinkCount()) + { + ExposeNode(shader_tmp, resources, port->GetLink(0)->Parent); + specular_code = port->GetLink(0)->Value; + } + else + { + specular_code = STR("0.0f"); + } + } + else if(port->Name == STR("SpecularPower")) + { + if(port->GetLinkCount()) + { + ExposeNode(shader_tmp, resources, port->GetLink(0)->Parent); + specular_power_code = port->GetLink(0)->Value; + } + else + { + specular_power_code = STR("0.0f"); + } + } + + shader = shader + shader_tmp + STR("\n"); + } + + String ps_input_struct = + "struct PS_INPUT \n" \ + "{\n"\ + " float4 Position : SV_Position;\n"\ + " float2 Texcoord0 : TEXCOORD0;\n"\ + " float4 Normal : TEXCOORD1;\n"\ + " float3 wPos : TEXCOORD2;\n"\ + " float3 Tangent : TEXCOORD3;\n"\ + " float3 Binormal : TEXCOORD4;\n"\ + " float4 ePos : TEXCOORD5;\n"\ + "};\n"; + + String ps_output_struct = + "struct PS_OUT\n" \ + "{\n" \ + " float4 color0 : SV_Target0;\n" \ + " float4 color1 : SV_Target1;\n" \ + " float2 color2 : SV_Target2;\n" \ + "};\n"; + + String final; + + String func = + "PS_OUT main_PS( PS_INPUT Input )\n{\n"\ + "PS_OUT output;\nfloat dist = length(Input.wPos - camPos);\nfloat ez = Input.Normal.w / camNearFarVPWH.y;\n"; + func += + "Input.Tangent.x*=-1; Input.Binormal.x*=-1;\n"; + + String out_albedo_refl = + "//albedo and reflection\noutput.color0 = float4(("; + out_albedo_refl = out_albedo_refl + diffuse_code + STR(").xyz,") + specular_code + STR(");\n"); + + String out_normal_gloss = + "//normal and glossiness\noutput.color1 = float4(("; + out_normal_gloss = out_normal_gloss + normal_code + STR(").xyz,") + specular_power_code + STR(");\n"); + + String out_depth = + "//depth info\noutput.color2 = float2(dist, ez);\n"; + + final = resources + per_object_cb + per_camera_cb + ps_input_struct + ps_output_struct + func + shader + out_albedo_refl + out_normal_gloss + out_depth + String("return output;\n}\n"); + + ps = final; + + //generate vertex shader + String shader_vs; + + //per object constant buffer + + shader_vs += per_object_cb; + + //per camera constant buffer + + shader_vs += per_camera_cb; + + //bones constant buffer (only for skinned meshes) + if((flags & SCF_SKINNING) != 0) + { + shader_vs += + "cbuffer skinData : register(b5)\n" \ + "{row_major matrix boneMatrix[MAX_BONES_COUNT];};\n"; + } + + //vertex shader input data structure + shader_vs += + "struct VS_INPUT {\n" \ + "float4 Position : POSITION;\n" \ + "float2 Texcoord : TEXCOORD;\n" \ + "float3 Normal : NORMAL;\n" \ + "float3 Tangent : TANGENT;\n" \ + "float3 Binormal : BINORMAL;\n"; + + //add skinning info + if((flags & SCF_SKINNING) != 0) + { + shader_vs += + "float4 BlendIndices : BLENDINDICES;\n" \ + "float4 BlendWeights : BLENDWEIGHT;\n"; + } + //close structure + shader_vs += "};\n"; + + //shader output data structure + shader_vs += + "struct VS_OUTPUT \n" \ + "{\n" \ + " float4 Position : SV_Position;\n" \ + " float2 Texcoord : TEXCOORD0;\n" \ + " float4 Normal : TEXCOORD1;\n" \ + " float3 wPos : TEXCOORD2;\n" \ + " float3 Tangent : TEXCOORD3;\n" \ + " float3 Binormal : TEXCOORD4;\n" \ + " float4 ePos : TEXCOORD5;\n" \ + "};\n"; + + //add skinning function for skinned meshes + if((flags & SCF_SKINNING) != 0) + { + shader_vs += + "float4 SkinVertex(float4 position, float4 index, float4 weight)\n" \ + "{\n" \ + " float4 pos = float4(0,0,0,0);\n" \ + " int IndexArray[4] = (int[4])(index);\n" \ + " float WeightArray[4] = (float[4])(weight);\n" \ + " float LastWeight = 0.0f;\n" \ + " int nb = 0;\n" \ + " for(int i = 0; i < 4;i++)\n" \ + " {\n" \ + " if(WeightArray[i]==0)\n" \ + " {\n" \ + " nb = i;\n" \ + " }\n" \ + " LastWeight = LastWeight + WeightArray[i];\n" \ + " pos = pos + mul(position, boneMatrix[IndexArray[i]] * WeightArray[i]);\n" \ + " }\n" \ + " pos = pos + mul(position, boneMatrix[IndexArray[nb]]* (1.0f - LastWeight));\n" \ + " return pos;\n" \ + "}\n"; + } + + //main vertex shader function + + //function header + shader_vs += + "VS_OUTPUT main_VS( VS_INPUT Input ){\n" \ + "VS_OUTPUT Output = (VS_OUTPUT)0;\n"; + + //vertex transformation (skinned/not skinned) + if((flags & SCF_SKINNING) != 0) //skinned + { + shader_vs += + "Output.Position = SkinVertex(Input.Position, Input.BlendIndices, Input.BlendWeights);\n" \ + "Output.wPos = mul(float4(Output.Position.xyz, 1.0f), mWorld);\n" \ + "Output.Position = mul(float4(Output.wPos.xyz, 1.0f), matVP);\n"; + } + else + { + shader_vs += + "Output.wPos = mul(float4(Input.Position.xyz, 1.0f), mWorld); \n" \ + "Output.Position = mul(float4(Output.wPos.xyz, 1.0f), matVP); \n"; + } + + //tail + shader_vs += + "Output.Texcoord = Input.Texcoord;\n" \ + "float3x3 mWorldNT = (float3x3)mWorld;\n" \ + "Output.ePos = Output.Position;\n" \ + "Output.Normal = float4(mul(Input.Normal, mWorldNT), mul(float4(Output.wPos, 1.0f), matView).z);\n" \ + "Output.Tangent = mul(Input.Tangent, mWorldNT);\n" \ + "Output.Binormal = mul(Input.Binormal, mWorldNT);\n" \ + + "return Output;\n" \ + "}\n"; + + vs = shader_vs; + + + *info = inputInfo; + } +} \ No newline at end of file diff --git a/LifeGraphics/shaders/ShaderGraphCompilerHLSL.h b/LifeGraphics/shaders/ShaderGraphCompilerHLSL.h new file mode 100644 index 0000000..8d0bb67 --- /dev/null +++ b/LifeGraphics/shaders/ShaderGraphCompilerHLSL.h @@ -0,0 +1,24 @@ +#ifndef ShaderGraphCompilerHLSL_h__ +#define ShaderGraphCompilerHLSL_h__ + +#include "shaders/IShaderGraphCompiler.h" + +namespace LifeGraphics +{ + class ShaderGraphCompilerHLSL : public IShaderGraphCompiler + { + public: + ShaderGraphCompilerHLSL(); + void CompileNode(IShaderGraphNode* rootNode, int flags, String& ps, String& vs, ShaderInputInfo* info); + private: + ShaderInputInfo inputInfo; + int lastTexSlot; + void RemoveChecked(IShaderGraphNode* node); + String NewVarName(const String& baseName); + int var_id; + void ExposeNode(String& shader, String& resources, IShaderGraphNode* node); + String TypeToStr(IShaderDataType t); + }; +} + +#endif // ShaderGraphCompilerHLSL_h__ \ No newline at end of file diff --git a/LifeGraphics/targetver.h b/LifeGraphics/targetver.h new file mode 100644 index 0000000..f22838f --- /dev/null +++ b/LifeGraphics/targetver.h @@ -0,0 +1,24 @@ +#pragma once + +// The following macros define the minimum required platform. The minimum required platform +// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run +// your application. The macros work by enabling all features available on platform versions up to and +// including the version specified. + +// Modify the following defines if you have to target a platform prior to the ones specified below. +// Refer to MSDN for the latest info on corresponding values for different platforms. +#ifndef WINVER // Specifies that the minimum required platform is Windows Vista. +#define WINVER 0x0600 // Change this to the appropriate value to target other versions of Windows. +#endif + +#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista. +#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows. +#endif + +#ifndef _WIN32_WINDOWS // Specifies that the minimum required platform is Windows 98. +#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later. +#endif + +#ifndef _WIN32_IE // Specifies that the minimum required platform is Internet Explorer 7.0. +#define _WIN32_IE 0x0700 // Change this to the appropriate value to target other versions of IE. +#endif \ No newline at end of file diff --git a/LifeGraphics/vegetation/PlantManager.h b/LifeGraphics/vegetation/PlantManager.h new file mode 100644 index 0000000..7ec44cb --- /dev/null +++ b/LifeGraphics/vegetation/PlantManager.h @@ -0,0 +1,607 @@ +#ifndef PlantManager_h__ +#define PlantManager_h__ + +#include "Globals.h" +#include "Terrain.h" +#include +#include "math/LifeMath.h" +#include +#include + +struct GRASS_INSTANCE_INFO +{ + LifeMath::float4 info; +}; +class GrassCacheBlock +{ +public: + LifeCore::BoundingBox3D bbox; + std::vector buffers; + int bw; + int cr; + int counters[4]; + + GrassCacheBlock(int blockWidth, int coverageResolution, int grassLayersCount) + { + bw = blockWidth; + cr = coverageResolution; + + int subBlocksStride = blockWidth / coverageResolution; + int subBlockCnt = subBlocksStride * subBlocksStride; + + int instanceCnt = subBlockCnt * 3; + + for(int i = 0; iGetRenderer()->CreateVertexBufferDynamic(instanceCnt * sizeof(GRASS_INSTANCE_INFO), sizeof(GRASS_INSTANCE_INFO))); + } + void UpdateBuffer(int plantId, GRASS_INSTANCE_INFO* tmpBuffer, int count) + { + counters[plantId] = count; + LifeGraphics::IVertexBuffer* vb = buffers[plantId]; + LifeCore::IDataStream* ds = vb->Map(0, LifeGraphics::LF_Discard); + ds->Write(tmpBuffer, count * sizeof(GRASS_INSTANCE_INFO)); + vb->Unmap(); + } + ~GrassCacheBlock() + { + for(size_t i = 0; i < buffers.size(); i++) + delete buffers[i]; + } +}; +class GridCell +{ +public: + LifeGraphics::Rectangle2D rect; + float minHeight; + float maxHeight; + std::vector instances; + int instanceCount; + GridCell() + :instanceCount(0) + { + } + ~GridCell() + { + instances.clear(); + } + void pushInstance(const GRASS_INSTANCE_INFO& instance) + { + instances.push_back(instance); + instanceCount++; + } +}; + +class PlantGrid +{ +public: + GridCell* cells; + int gridSize; + int cellSize; + int cellStride; + std::vector visibleCells; + + void pushInstance(const GRASS_INSTANCE_INFO& info) + { + int id = getCellId(info.info); + getCell(id)->pushInstance(info); + } + int getCellId(const LifeMath::float3& position) + { + int pX = static_cast(floorf(position.X / static_cast(cellSize))); + int pY = static_cast(floorf(-position.Z / static_cast(cellSize))); + + if(pX < 0 || pY < 0) return -1; + if(pX >= cellStride || pY >= cellStride) return -1; + + return pY * cellStride + pX; + } + int getCellId(const LifeMath::float4& position) + { + int pX = static_cast(floor(position.X / static_cast(cellSize))); + int pY = static_cast(floor(-position.Z / static_cast(cellSize))); + + if(pX < 0 || pY < 0) return -1; + if(pX >= cellStride || pY >= cellStride) return -1; + + return pY * cellStride + pX; + } + + GridCell* getCell(int id) + { + return cells + id; + } + + PlantGrid(int _gridSize, int _cellSize) + { + gridSize = _gridSize; + cellSize = _cellSize; + + cellStride = gridSize / cellSize; + int cellCount = cellStride * cellStride; + cells = new GridCell[cellCount]; + + for(int y = 0; y < cellStride; y++) + { + for(int x = 0; x < cellStride; x++) + { + GridCell* c = cells + (y * cellStride + x); + + c->rect.minX = (float)(x * cellSize); + c->rect.maxX = (float)(x * cellSize) + (float)cellSize; + + c->rect.minY = -((float)(y * cellSize)); + c->rect.maxY = -((float)(y * cellSize) + (float)cellSize); + + c->minHeight = 0.0f; + c->maxHeight = 255.0f; + } + } + } + ~PlantGrid() + { + visibleCells.clear(); + delete[] cells; + } + + void Update(LifeGraphics::Camera* cam) + { + visibleCells.clear(); + for(int y = 0; y< cellStride; y++) + { + for(int x = 0; x< cellStride; x++) + { + int offset = y * cellStride + x; + GridCell* c = cells + offset; + LifeMath::float3 vmin(c->rect.minX, 0.0f, c->rect.maxY); + LifeMath::float3 vmax(c->rect.maxX, 255.0f, c->rect.minY); + LifeCore::BoundingBox3D box(vmin, vmax); + + if(cam->getFrustum()->BoxInFrustum(box)) + { + visibleCells.push_back(c); + } + } + } + // + } +}; + +struct PLANT_BEND_PARAMS +{ + float fBendScale; + float fBendSpeed; + LifeMath::float2 dummy; +}; + +class PlantClass +{ +private: + LifeGraphics::Terrain* terra; +public: + PlantGrid* grid; + LifeGraphics::IShader* imposterPsh; + LifeGraphics::IShader* imposterVsh; + LifeGraphics::IConstantBuffer* plantBendInfo; + LifeGraphics::ITexture2D* imposterTextures[9]; + PlantClass(LifeGraphics::Terrain* _terra, LifeGraphics::IMesh* mesh) + { + imposterVsh = LifeGraphics::ResourceManager::me()->CompileUserShaderVS(AS_STRING("Plant_simple.vsh")); + imposterPsh =LifeGraphics:: ResourceManager::me()->CompileUserShaderPS(AS_STRING("Plant_simple.psh")); + + terra =_terra; + plantCount = 0; + plantGeom = mesh; + grid = new PlantGrid(_terra->TerrainWidth, 64); + plantBendInfo = LifeGraphics::CG::me()->GetRenderer()->CreateConstantBuffer(sizeof(PLANT_BEND_PARAMS)); + PLANT_BEND_PARAMS params; + // params.fBendScale = 1.0f / mesh->getBoundingBox()->sizeY() * 0.15f; //FIXME!!!!!!!!!!!!!!!!!!!!!!!!!!! + params.fBendSpeed = 0.7f; + plantBendInfo->Update(¶ms, sizeof(PLANT_BEND_PARAMS)); + + //generate textures for imposter rendering + } + ~PlantClass() + { + SAFE_DELETE(grid); + SAFE_DELETE(plantBendInfo); + SAFE_DELETE(plantGeom); + } + void AddInstance(const GRASS_INSTANCE_INFO& instance) + { + grid->pushInstance(instance); + } + LifeGraphics::IMesh* plantGeom; + int plantCount; +}; + +class PlantManager +{ +private: + int currentCell; + LifeGraphics::Terrain* terra; + byte* coverageMask; + PlantGrid* grid; + std::vector buffers; + std::vector cache; + std::vector validBlocks; + std::vector visibleBlocks; + LifeGraphics::IVertexBuffer* plantInstBuf; + int blockSize; + int cacheStride; + int blockStride; + std::vector models; + std::vector plants; + LifeGraphics::IShader* grassVs; + LifeGraphics::IShader* grassPs; + LifeGraphics::IShader* grassPs_g; + LifeGraphics::IShader* plantTrunkVs; + LifeGraphics::IShader* plantLeafVs; + + LifeGraphics::IConstantBuffer* grassBendInfo; + LifeGraphics::IRasterizerState* rsCullNone; + LifeGraphics::IRasterizerState* rsCullBack; + + LifeGraphics::IInputLayout* layout; + + LifeGraphics::texture_ptr tex; + +public: + PlantManager() + :currentCell(-1), blockSize(16), cacheStride(3) + { + } + + ~PlantManager() + { + SAFE_DELETE(grassBendInfo); + SAFE_DELETE(rsCullNone); + SAFE_DELETE(rsCullBack); + SAFE_DELETE(plantInstBuf); + SAFE_DELETE(grassVs); + SAFE_DELETE(grassPs); + SAFE_DELETE(grassPs_g); + SAFE_DELETE(plantLeafVs); + SAFE_DELETE(plantTrunkVs); + SAFE_DELETE(grassBendInfo); + delete[]coverageMask; + SAFE_DELETE(grid); + //SAFE_DELETE(layout); + + tex->Release(); + + //delete plants + int pc = plants.size(); + for(int i = 0; iGetRenderer(); + renderer->SetRasterizerState(rsCullNone); + renderer->SetPSTexture(tex.ptr(), 0); + + //render grass + if(pass != LifeGraphics::PT_RENDER_SHADOW_MAP_PSSM) + { + renderer->SetPrimitiveTopology(PT_TRIANGLE_LIST); + renderer->SetVertexShader(grassVs); + renderer->SetVSConstantBuffer(grassBendInfo, 10); + renderer->SetInputLayout(layout); + + int cnt = visibleBlocks.size(); + for(int i = 0; icounters[j]; + if(plantCount > 0) + { + LifeGraphics::IMesh* plant = models[j]; + renderer->SetIndexBuffer(plant->indexData); + LifeGraphics::IVertexBuffer* buffers[2]; + buffers[0] = plant->vertexData; + buffers[1] = cb->buffers[j]; + + renderer->SetVertexBuffer(buffers,2); + + if(pass == LifeGraphics::PT_COMPOSE_G_BUF) + renderer->SetPixelShader(grassPs_g); + else if(pass == LifeGraphics::PT_COMPOSE_FINAL) + renderer->SetPixelShader(grassPs); + + renderer->DrawIndexedInstanced(0, plant->subsets[0].Length, 0, plantCount); + } + } + } + } + + //render other plants + for(size_t i = 0; igrid->visibleCells.size(); + + renderer->SetVSConstantBuffer(c->plantBendInfo, 10); + for(int j = 0; jgrid->visibleCells[j]; + + int plantCount = cell->instanceCount; + if(plantCount > 0) + { + LifeCore::IDataStream* buf = plantInstBuf->Map(0, LifeGraphics::LF_Discard); + + buf->Write(&cell->instances[0], plantCount * sizeof(GRASS_INSTANCE_INFO)); + plantInstBuf->Unmap(); + + LifeGraphics::IMesh* plant = c->plantGeom; + renderer->SetIndexBuffer(plant->indexData); + LifeGraphics::IVertexBuffer* buffers[2]; + buffers[0] = plant->vertexData; + buffers[1] = plantInstBuf; + + renderer->SetVertexBuffer(buffers,2); + + if(pass != LifeGraphics::PT_RENDER_SHADOW_MAP_PSSM) + { + if(pass == LifeGraphics::PT_COMPOSE_G_BUF) + renderer->SetPixelShader(grassPs_g); + else if(pass == LifeGraphics::PT_COMPOSE_FINAL) + renderer->SetPixelShader(grassPs); + } + + for(int s = 0; ssubsetCount; s++) + { + if(s==0) + { + renderer->SetVertexShader(plantLeafVs); + } + else + { + renderer->SetVertexShader(plantTrunkVs); + } + //renderer->SetPSTexture(plant->subsets[s].material.diffuseMap.ptr(),0); + + renderer->DrawIndexedInstanced(plant->subsets[s].Offset, plant->subsets[s].Length, 0, plantCount); + } + } + } + } + LifeGraphics::CG::me()->GetRenderer()->SetRasterizerState(rsCullBack); + } + + void Update(float timeElapsed, LifeGraphics::Camera* cam) + { + //update plants + int plantCount = plants.size(); + for(int i = 0; i< plantCount; i++) + { + PlantClass* c = plants[i]; + c->grid->Update(cam); + } + + //update grass + LifeMath::float3 pos = cam->getPosition(); + int cell = grid->getCellId(pos); + + int offset[] = {0,0,0,0}; + + if(cell != currentCell ) + { + validBlocks.clear(); + currentCell = cell; + if(currentCell != -1) + { + //cell has been changed! regenerate plant buffer + int cellOffset = (cacheStride - 1) / 2; + + int col = static_cast(floor(pos.X / static_cast(blockSize))); + int row = static_cast(floor(-pos.Z / static_cast(blockSize))); + + //scan each cache block + for(int y = (row - cellOffset); y <= (row + cellOffset); y++) + { + for(int x = (col - cellOffset); x <= (col + cellOffset); x++) + { + int check = y * blockStride + x; + //if we are not out of terrain bounds + if(y>=0 && x >=0 && y < blockStride && x< blockStride) + { + int cacheOffset = (y - row + cellOffset) * cacheStride + (x - col + cellOffset); + GrassCacheBlock* cb = cache[cacheOffset]; + GridCell* cell = grid->getCell(check); + validBlocks.push_back(cb); + + //null all counters + for(int k = 0; k<4; k++) + { + offset[k] = 0; + } + + LifeMath::float3 vmin(cell->rect.minX, 0.0f, cell->rect.maxY); + LifeMath::float3 vmax(cell->rect.maxX, 255.0f, cell->rect.minY); + + cb->bbox.resize(vmin, vmax); + //cell is selected//run through it's sub cells + for(int sy = -(int)cell->rect.minY; sy < -((int)cell->rect.maxY); sy++) + { + for(int sx = (int)cell->rect.minX; sx < (int)cell->rect.maxX; sx++) + { + byte pd = coverageMask[sy * terra->TerrainWidth + sx]; + + for(int i = 0; i<4; i++) + { + //read plants count for i grass type + byte cnt = (pd >> (2 * i)) & (0x3); + + //and finally generate plants info + for(byte j = 0; j < (cnt); j++) + { + srand(sy * sx *(i + 1 + j)); + GRASS_INSTANCE_INFO ni; + ni.info.X = (float)sx + ((float)(rand() % 100)) / 100.0f; + ni.info.Z = -((float)sy + ((float)(rand() % 100)) / 100.0f); + ni.info.Y = terra->GetHeight(ni.info.X, ni.info.Z); + ni.info.W = 1.0f; + buffers[i][offset[i]] = ni; + offset[i] = offset[i] + 1; + } + } + } + } + for(int l = 0; l<4; l++) + { + cb->UpdateBuffer(l, buffers[l],offset[l]); + } + } + } + } + } + } + + int vc = validBlocks.size(); + visibleBlocks.clear(); + for(int i = 0; igetFrustum()->BoxInFrustum(validBlocks[i]->bbox)) + visibleBlocks.push_back(validBlocks[i]); + } + + /* printf("Visible grass chunks: %i\n", visibleBlocks.size()); + for(int i = 0; icounters[j] ); + } + }*/ + } + + void Init(LifeGraphics::Terrain* _terra) + { + grassBendInfo = LifeGraphics::CG::me()->GetRenderer()->CreateConstantBuffer(sizeof(PLANT_BEND_PARAMS)); + PLANT_BEND_PARAMS params; + params.fBendScale = 0.09f; + params.fBendSpeed = 0.5f; + + grassBendInfo->Update(¶ms, sizeof(PLANT_BEND_PARAMS)); + + rsCullNone = LifeGraphics::CG::me()->GetRenderer()->CreateRasterizerState(FILL_SOLID, CULL_NONE); + rsCullBack = LifeGraphics::CG::me()->GetRenderer()->CreateRasterizerState(FILL_SOLID, CULL_BACK); + + plantInstBuf = LifeGraphics::CG::me()->GetRenderer()->CreateVertexBufferDynamic(128 * sizeof(GRASS_INSTANCE_INFO), sizeof(GRASS_INSTANCE_INFO)); + + grassVs = LifeGraphics::ResourceManager::me()->CompileUserShaderVS(AS_STRING("Grass.vsh")); + grassPs = LifeGraphics::ResourceManager::me()->CompileUserShaderPS(AS_STRING("Grass.psh")); + grassPs_g = LifeGraphics::ResourceManager::me()->CompileUserShaderPS(AS_STRING("Grass_g.psh")); + + plantLeafVs = LifeGraphics::ResourceManager::me()->CompileUserShaderVS(AS_STRING("Plant_leafs.vsh")); + plantTrunkVs = LifeGraphics::ResourceManager::me()->CompileUserShaderVS(AS_STRING("Plant_trunk.vsh")); + + tex = LifeGraphics::ResourceManager::me()->GetTexture(AS_STRING("root/Data/Textures/cst_grass_long.dds")); + + terra = _terra; + coverageMask = new byte[terra->TerrainWidth * terra->TerrainWidth]; + for(int i = 0; i< (terra->TerrainWidth * terra->TerrainWidth); i++) + { + coverageMask[i] = ((byte)(0x3 << 6)) | ((byte)(0x0 << 4)) | ((byte)(0x0 << 2)) | ((byte)(0x0 << 0)); + } + + //memset(coverageMask, 0, terra->TerrainWidth * terra->TerrainWidth); + + grid = new PlantGrid(terra->TerrainWidth, blockSize); + + for(int i = 0; i<4; i++) + buffers.push_back(new GRASS_INSTANCE_INFO[blockSize*blockSize*3]); + + blockStride = terra->TerrainWidth / blockSize; + + for(int i = 0; i< (cacheStride * cacheStride); i++) + { + cache.push_back(new GrassCacheBlock(blockSize,1,4)); + } + + //load grass meshes + LifeGraphics::IMesh* tm = LifeGraphics::ResourceManager::me()->GetMesh(AS_STRING("root/Data/Objects/grass_a.vmf")); + models.push_back(tm); + models.push_back(tm); + models.push_back(tm); + models.push_back(tm); + /*models.push_back(ResourceManager::me()->GetMesh(AS_STRING("root/Data/Objects/g_1.vmf"))); + models.push_back(ResourceManager::me()->GetMesh(AS_STRING("root/Data/Objects/g_2.vmf"))); + models.push_back(ResourceManager::me()->GetMesh(AS_STRING("root/Data/Objects/g_3.vmf")));*/ + + LifeGraphics::IInputElement* elements = new LifeGraphics::IInputElement[6]; + elements[0] = LifeGraphics::IInputElement("POSITION", 0, 0, FORMAT_R32G32B32_FLOAT, 0, 0, 0); + elements[1] = LifeGraphics::IInputElement("TEXCOORD", 0, 12, FORMAT_R32G32_FLOAT, 0, 0, 0); + elements[2] = LifeGraphics::IInputElement("NORMAL", 0, 20, FORMAT_R32G32B32_FLOAT, 0, 0, 0); + elements[3] = LifeGraphics::IInputElement("TANGENT", 0, 32, FORMAT_R32G32B32_FLOAT, 0, 0, 0); + elements[4] = LifeGraphics::IInputElement("BINORMAL", 0, 44, FORMAT_R32G32B32_FLOAT, 0, 0, 0); + elements[5] = LifeGraphics::IInputElement("INSTANCEDATA", 0, 0, FORMAT_R32G32B32A32_FLOAT, 1, 1, 1); + + layout = LifeGraphics::CG::me()->GetRenderer()->CreateInputLayout(elements, 6, grassVs); + //fix up models declaration + for(int i = 0; i<1; i++) + { + LifeGraphics::IMesh* m = models[i]; + m->inputLayout = layout; + } + + LifeGraphics::IMesh* elka = LifeGraphics::ResourceManager::me()->GetMesh(AS_STRING("root/Data/Objects/Tree_test.vmf")); + + //IMesh* elka = ResourceManager::me()->GetMesh(AS_STRING("root/Data/Objects/Tree_test.vmf")); + //elka->subsets[0].material.diffuseMap = ResourceManager::me()->GetTexture(AS_STRING("root/Data/Textures/Tree_2_2.dds")); + //elka->subsets[2].material.diffuseMap = ResourceManager::me()->GetTexture(AS_STRING("root/Data/Textures/BARK5.jpg")); + // elka->subsets[1].material.diffuseMap = ResourceManager::me()->GetTexture(AS_STRING("root/Data/Textures/BARK5.jpg")); + //elka->subsets[0].material.diffuseMap = ResourceManager::me()->GetTexture(AS_STRING("root/Data/Textures/cod5_tree_branch.dds")); + AddPlant(elka); + PlantClass* pc = plants[0]; + GRASS_INSTANCE_INFO tree; + + for(int x = 0; x<10; x++) + { + for(int y = 0; y<10; y++) + { + tree.info = LifeMath::float4( (float)x*6.0f + 100.0f,terra->GetHeight((float)x*6.0f + 100.0f, -(float)y*6.0f - 20.0f), -(float)y*6.0f - 20.0f, 1.0f ); + pc->AddInstance(tree); + } + } + + //tree.info = LifeMath::float4(70.0f,terra->GetHeight(70.0f, -20.0f), -20.0f, 1.0f ); + //pc->AddInstance(tree); + } +}; +#endif // PlantManager_h__ \ No newline at end of file diff --git a/LifeMaterialEditor/Editor.cpp b/LifeMaterialEditor/Editor.cpp new file mode 100644 index 0000000..8103c5b --- /dev/null +++ b/LifeMaterialEditor/Editor.cpp @@ -0,0 +1,597 @@ +#include "Editor.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gui/GUIToolStrip.h" +#include "gui/GUIToolStripButton.h" +#include +#include +#include +#include + +#include "EditorShared.h" +#include +#include +#include +#include +#include + +#include +#include + +#include "GUIShaderNode.h" +#include "GUIShaderNodeIO.h" + +#include + +Editor::Editor() + :bRunning(false) +{ +} +Editor::~Editor() +{ +} +void Editor::menu_exit(GUIMenuItem* m) +{ + printf("Exit!!!!!!!\n"); + exit(0); +} +void Editor::AddShaderLink(GUIShaderNodeIO* left, GUIShaderNodeIO* right) +{ + //make spline link + nodeContainer->MakeLink(left, right); + + if(right->IsInput()) + { + GUIShaderNodeIO* tmp = right; + right = left; + left = tmp; + } + + //do shader graph connection + IShaderGraphNode* leftNode = left->Owner->GetGraphicsNode(); + IShaderGraphNode* rightNode = right->Owner->GetGraphicsNode(); + + IShaderGraphNodePort* leftPort = leftNode->Inputs[left->id]; + IShaderGraphNodePort* rightPort = rightNode->Outputs[right->id]; + leftPort->MakeLink(leftPort, rightPort); +} +void Editor::OnButtonClick(GUIWindow* wnd, int key) +{ + GUIButton* b = reinterpret_cast(wnd); + b->SetCaption(b->GetCaption() + STR("/")); +} +void Editor::OnScroll(GUIScroll* wnd, int key) +{ + char buf[32]; + int val = wnd->GetValue(); + itoa(val, &buf[0], 10); + String s(buf); + b2->SetCaption(s); +} +void Editor::SwitchPlayback(GUIWindow* wnd, int t) +{ + if(snd->IsPlaying()) + { + snd->Stop(); + reinterpret_cast(wnd)->SetCaption(STR("Play")); + } + else + { + snd->Play(); + reinterpret_cast(wnd)->SetCaption(STR("Stop")); + } +} +void Editor::OnResizeOutput(GUIWindow* wnd) +{ + printf("!!!!!!!!OMG!!! OUTPUT RESIZE!!!!\n"); + Rect2i r = sceneOutput->GetClientRectangleAbs(); + + pGraphics->GetSceneManager()->GetActiveScene()->ResizeOutput(r.width, r.height); + + CImage* img = (CImage*)sceneOutput->GetPicture(); + if(img) + { + img->Reset(pGraphics->GetSceneManager()->GetActiveScene()->GetOutput().ptr()); + sceneOutput->SetPicture(img); + } +} + +GUIShaderNode* Editor::AddNode(const String& name) +{ + GUIShaderNode* node = nodeContainer->AddNode(name); + node->SetPosition(Vec2i(10,10)); + node->Event_Attach += NewDelegate(this, &Editor::AddShaderLink); + return node; +} +void Editor::OnAddNode( GUIWindow* w, const String& s, void* d) +{ + int id = (int)d; + NodeInfo* info = node_info[id]; + GUIShaderNode* node = this->AddNode(info->ClassName); + node->SetCaption(info->Name); +} + +void Editor::OnSaveGraph(GUIWindow* wnd, int p) +{ + Core::IFile* graph_file; + Core::IFile* gui_file; + + //save shader graph + if(ResourceManager::me()->getFileSystem()->GetFile("root/Data/MyMaterial.sg", &graph_file) != SC_OK){ + ResourceManager::me()->getFileSystem()->NewFile("root/Data/MyMaterial.sg", 0); + ResourceManager::me()->getFileSystem()->GetFile("root/Data/MyMaterial.sg", &graph_file); + } + IDataStreamPtr stm = graph_file->OpenWrite(0); + IMemoryStream mem_stm(1024*128); + sg->Serialize(&mem_stm); + stm->Write(mem_stm.GetData(), mem_stm.Tell()); + stm->Close(); + + //save GUI extra graph data + if(ResourceManager::me()->getFileSystem()->GetFile("root/Data/MyMaterial.ext", &gui_file) != SC_OK){ + ResourceManager::me()->getFileSystem()->NewFile("root/Data/MyMaterial.ext", 0); + ResourceManager::me()->getFileSystem()->GetFile("root/Data/MyMaterial.ext", &gui_file); + } + stm = gui_file->OpenWrite(0); + mem_stm.Seek(0); + nodeContainer->Serialize(&mem_stm); + stm->Write(mem_stm.GetData(), mem_stm.Tell()); + stm->Close(); + +} + +void Editor::OnGraphNodeDelete(GUINodeContainer* container, GUIShaderNode* node) +{ + if(EditorShared::me()->propGrid->GetTarget() == + (Core::IPropertyHolder*)node) + { + EditorShared::me()->propGrid->SetTarget(nullptr); + } +} + +void Editor::OnLoadGraph(GUIWindow* wnd, int p) +{ + Core::IFile* graph_file ; + if(ResourceManager::me()->getFileSystem()->GetFile("root/Data/MyMaterial.sg", &graph_file) != SC_OK) + return; + IDataStreamPtr stm = graph_file->OpenRead(); + char* buffer = new char[stm->Length()]; + stm->Read(buffer, stm->Length()); + IMemoryStream mem_stm(stm->Length()); + mem_stm.Write(buffer, stm->Length()); + mem_stm.Seek(0); + sg->Clear(); + sg->Deserialize(&mem_stm); + nodeContainer->RebuildGraph(); + int cnt = nodeContainer->GetNodeCount(); + for(int i = 0; i < cnt; i++){ + nodeContainer->getNode(i)->Event_Attach += NewDelegate(this, &Editor::AddShaderLink); + } + stm->Close(); + delete[] buffer; + Core::IFile* gui_file ; + if(ResourceManager::me()->getFileSystem()->GetFile("root/Data/MyMaterial.ext", &gui_file) != SC_OK) + return; + stm = gui_file->OpenRead(); + buffer = new char[stm->Length()]; + stm->Read(buffer, stm->Length()); + IMemoryStream ext_stm(stm->Length()); + ext_stm.Write(buffer, stm->Length()); + ext_stm.Seek(0); + nodeContainer->Deserialize(&ext_stm); + stm->Close(); + delete[] buffer; +} +void Editor::OnCompileShader(GUIWindow* wnd, int p) +{ + IMaterial* mat = new IMaterial(sg, new ShaderGraphCompilerHLSL()); + + NodeCollection* texNodes = nodeContainer->GetTextureUnits(); + for(int i = 0; i < (*texNodes).size(); i++) + { + GUIShaderNodeTexture* tex = (GUIShaderNodeTexture*)((*texNodes)[i]); + texture_ptr texture = ResourceManager::me()->GetTexture(tex->GetFilename()); + mat->SetTexture(tex->GetGraphicsNode()->id, texture); + } + + + tower->setMaterial(mat, 0, false); + //tower->getMaterial(0)-> + //assert(false && "Not implemented yet"); + /*ShaderGraphCompilerHLSL* shaderCompiler = new ShaderGraphCompilerHLSL(); + String ps, vs; + shaderCompiler->CompileNode(sg->GetNodeByID(0), NULL, ps, vs); + + IShader* pixelShader = CG::me()->GetRenderer()->CompilePixelShader(ps.c_str(), ps.length()); + IShader* vertexShader = CG::me()->GetRenderer()->CompileVertexShader(vs.c_str(), vs.length()); + + //grab texture info from graph + IMaterial* mat = new IMaterial(); + mat->g_buffer_pass_ps = pixelShader; + mat->g_buffer_pass_vs = vertexShader; + + NodeCollection* texNodes = nodeContainer->GetTextureUnits(); + for(int i = 0; i < (*texNodes).size(); i++) + { + GUIShaderNodeTexture* tex = (GUIShaderNodeTexture*)((*texNodes)[i]); + texture_ptr texture = ResourceManager::me()->GetTexture(tex->FileName()); + mat->SetTexture(texture, tex->GraphicsNode->resource_id); + } + + tower->setMaterial(mat, 0, false);*/ +} +void Editor::Run() +{ + node_info.push_back(new NodeInfo(STR("Float1"), STR("ConstFloat1"))); + node_info.push_back(new NodeInfo(STR("Float2"), STR("ConstFloat2"))); + node_info.push_back(new NodeInfo(STR("Float3"), STR("ConstFloat3"))); + node_info.push_back(new NodeInfo(STR("Float4"), STR("ConstFloat4"))); + + node_info.push_back(new NodeInfo(STR("Add"), STR("OpAdd"))); + node_info.push_back(new NodeInfo(STR("Sub"), STR("OpSub"))); + node_info.push_back(new NodeInfo(STR("Mul"), STR("OpMul"))); + node_info.push_back(new NodeInfo(STR("Div"), STR("OpDiv"))); + node_info.push_back(new NodeInfo(STR("Lerp"), STR("OpLerp"))); + node_info.push_back(new NodeInfo(STR("Normalize"), STR("OpNormalize"))); + node_info.push_back(new NodeInfo(STR("Saturate"), STR("OpSaturate"))); + node_info.push_back(new NodeInfo(STR("Power"), STR("OpPow"))); + node_info.push_back(new NodeInfo(STR("Texture2D"), STR("SrcTex2D"))); + node_info.push_back(new NodeInfo(STR("TEXCOORD"), STR("SrcUV"))); + node_info.push_back(new NodeInfo(STR("DecodeNormal"), STR("DecodeNormal"))); + node_info.push_back(new NodeInfo(STR("Time"), STR("Time"))); + + IShaderGraphNodeFactory* shaderFactory = IShaderGraphNodeFactory::me(); + IShaderFactorySource::Register(shaderFactory); + sg = new IShaderGraph(shaderFactory); + + + + //getchar(); + //SetCurrentDirectory("C:\\development\\LifeProject\\Release"); + /*CLog::me()->LogToConsole(true); + ResourceManager::me()->SetCentralDirectory(STR("Resources\\")); + + IFile* f; + + if(SC_OK != ResourceManager::me()->getFileSystem()->GetFile("root/Resources/Data.pak", &f)) + { + printf("Can not load Data.pak!"); + system("pause"); + return; + } + + IDataStreamPtr pr = f->OpenRead(); + + IPakFileContentProvider pp(pr.get(), STR("Data")); //pp() _text("Data.pak"), STR("Data") + String mp(_text("root")); + ResourceManager::me()->MountContentProvider(&pp, mp);*/ + ResourceManager::me()->SetCentralDirectory(STR("D:\\development\\Programming\\Projects\\Data\\")); + + pWindow = new RenderWindowWin(); + pWindow->SetSize(900, 600); + pWindow->CenterWindow(); + pWindow->SetCursorVisible(false); + + pInput = new InputSystemOIS(); + pInput->Init(900, 600, false, pWindow->GetWindowHandle()); + pInput->AddListener(this); + + Video::RenderParams params; + params.AllowSwitchToSoftware = false; + params.ForceWideScreen = false; + params.ScreenWidth = 900; + params.ScreenHeight = 600; + params.Window = pWindow; + params.Windowed = true; + + pGraphics = new LifeEngine(); + pGraphics->Init(RT_DIRECTX_11, params); + + pGraphics->GetGUI()->AddOverlay("GUI"); + + GUIOverlay* ovrl = pGraphics->GetGUI()->GetOverlay("GUI"); + + + nodeContainer = new GUINodeContainer(sg); + nodeContainer->Event_DeleteNode += NewDelegate(this, &Editor::OnGraphNodeDelete); + + GUIToolStrip* strp = new GUIToolStrip(); + strp->SetDock(DOCK_TOP); + strp->SetHeight(20); + strp->SetWidth(20); + ovrl->AddChild(strp); + //strp->SetBackColor(Vec4f(0.9f, 0.5f, 0.5f, 1.0f)); + + GUIToolStripMenu* fileMenu = new GUIToolStripMenu(); + + fileMenu->SetCaption(STR("File")); + + GUIMenu* fm = fileMenu->GetMenu(); + GUIMenu* fm2; + fm->AddItem(STR("Open"), false); + fm2 = fm->AddItem(STR("Save as"), true)->GetSubmenu(); + fm->AddItem(STR("Recent"), false); + fm->AddItem(STR("Exit"), false)->Event_Select += NewDelegate(this, &Editor::menu_exit); + + fm2->AddItem(STR("BMP"), false); + fm2->AddItem(STR("DDS"), false); + fm2->AddItem(STR("OMG"), false); + + strp->AddItem(fileMenu); + + GUIToolStripButton* tsb = new GUIToolStripButton(); + tsb->AsButton()->SetCaption("Compile shader"); + tsb->AsButton()->Event_MouseClick += NewDelegate(this, &Editor::OnCompileShader); + strp->AddItem(tsb); + + GUIToolStripButton* tsb2 = new GUIToolStripButton(); + strp->AddItem(tsb2); + + GUIToolStripButton* tsb3 = new GUIToolStripButton(); + strp->AddItem(tsb3); + + GUIToolStripImage* tsim = new GUIToolStripImage(); + strp->AddItem(tsim); + + GUIPicture* pic = (GUIPicture*)tsim->GetInternalWindow(); + pic->SetPicture(GUIManager::me()->GetRenderer()->GetImage(STR("save_icn.bmp"))); + + + GUIToolStripButton* save_button = new GUIToolStripButton(); + save_button->AsButton()->SetCaption("Save"); + save_button->AsButton()->Event_MouseClick += NewDelegate(this, &Editor::OnSaveGraph); + strp->AddItem(save_button); + + + GUIToolStripButton* load_button = new GUIToolStripButton(); + load_button->AsButton()->SetCaption("Load"); + load_button->AsButton()->Event_MouseClick += NewDelegate(this, &Editor::OnLoadGraph); + strp->AddItem(load_button); + + + GUIScrollablePlane* p11 = new GUIScrollablePlane(); + p11->SetWidth(900); + p11->SetHeight(600); + p11->SetDock(DOCK_FILL); + p11->SetPosition(LifeMath::Vec2i(0, 0)); + ovrl->AddChild(p11); + p11->GetVScroll()->Event_Scroll += NewDelegate(this, &Editor::OnScroll); + p11->SetBorderEnabled(false); + + sndSystem = new Sound::SoundSystem(); + if(sndSystem->Init(ResourceManager::me()->getFileSystem()) != 0) + { + printf("Error initializing sound system!"); + } + else + printf("Sound system ready!\n"); + + snd = sndSystem->getSound(STR("root/Data/Sounds/Wwise_Main_Menu_Streaming.005.ogg"), Sound::SM_ENABLED); + + GUINodeList* node_list = new GUINodeList(); + node_list->SetDock(DOCK_RIGHT); + node_list->SetWidth(150); + node_list->Event_ItemSelected += NewDelegate(this, &Editor::OnAddNode); + + int node_cnt = node_info.size(); + for(int i = 0; i < node_cnt; i++) + { + node_list->AddItem(node_info[i]->Name, (void*)i); + } + + AddNode(STR("Material"))->SetCaption(STR("Material"));; + p11->AddChild(node_list); + + GUISplitPanel* sp = new GUISplitPanel(); + p11->AddChild(sp); + sp->SetWidth(900); + sp->SetHeight(600); + sp->SetDock(DOCK_FILL); + sp->SetPosition(Vec2i(0,0)); + sp->SetSplitterHorisontal(false); + sp->SetRealtimeResize(true); + sp->SetSplitterPosition(450); + + GUISplitPanel* sp2 = new GUISplitPanel(); + sp->GetContainer1()->AddChild(sp2); + sp2->SetWidth(300); + sp2->SetHeight(300); + sp2->SetPosition(Vec2i(80,80)); + sp2->SetSplitterHorisontal(true); + sp2->SetRealtimeResize(true); + sp2->SetDock(Video::DOCK_FILL); + sp2->SetBorderEnabled(false); + sp->SetBorderEnabled(false); + + sp->GetContainer2()->AddChild(nodeContainer); + nodeContainer->SetDock(DOCK_FILL); + + sceneOutput = new GUIPicture(); + sceneOutput->SetPosition(Vec2i(2,2)); + sceneOutput->SetWidth(200); + sceneOutput->SetHeight(160); + sceneOutput->SetDock(DOCK_FILL); + sceneOutput->Event_Resize += NewDelegate(this, &Editor::OnResizeOutput); + sp2->GetContainer1()->AddChild(sceneOutput); + + + sp2->GetContainer1()->SetBackColor(Vec4f(0.3f, 0.3f, 0.3f, 1.0f)); + sp2->GetContainer2()->SetBackColor(Vec4f(0.3f, 0.3f, 0.3f, 1.0f)); + sp->GetContainer1()->SetBackColor(Vec4f(0.3f, 0.3f, 0.3f, 1.0f)); + sp->GetContainer2()->SetBackColor(Vec4f(0.3f, 0.3f, 0.3f, 1.0f)); + + + CImage* img = CImage::FromTexture(ResourceManager::me()->GetTexture(STR("root/Data/Textures/me_small.jpg")).ptr()); + sceneOutput->SetPicture(img); + sceneOutput->SetWidth(img->GetWidth() + 2); + sceneOutput->SetHeight(img->GetHeight() + 2); + sceneOutput->SetBackColor(Vec4f(0,0,0,1)); + + tower = new Video::IObjectMesh("root/Data/Objects/Tower_opt.vmf"); + pGraphics->GetSceneManager()->GetActiveScene()->PushNode(tower); + tower->getTransform()->SetPosition(Vec3f(0,0,0)); + + Video::Light* l = new Video::Light(Video::LT_POINT); + l->setRange(20); + pGraphics->GetSceneManager()->GetActiveScene()->PushNode(l); + l->getTransform()->SetPosition(Vec3f(4,0.5f,-4.0f)); + l->color = Vec3f(1,0,0); + l->intensity = 1.0f; + + propGrid = new GUIPropertyGrid(); + propGrid->SetWidth(300); + propGrid->SetHeight(300); + propGrid->AddEditor(new GUIFilePathPropertyEditor(propGrid)); + propGrid->SetDock(DOCK_FILL); + + sp2->GetContainer2()->AddChild(propGrid); + EditorShared::me()->propGrid = propGrid; + + pGraphics->GetSceneManager()->GetActiveScene()->mainCam->setAspect(1.3333f); + pGraphics->GetSceneManager()->GetActiveScene()->mainCam->setFar(100.0f); + pGraphics->GetSceneManager()->GetActiveScene()->mainCam->setNear(0.1f); + pGraphics->GetSceneManager()->GetActiveScene()->mainCam->setFov(3.1415f / 3.0f); + + /*GUIList* testList = new GUIList(); + + for(int i = 0; i < 50; i++) + { + testList->AddItem(STR("Trololo1"), 0); + } + ovrl->AddChild(testList); + testList->SetWidth(200); + testList->SetHeight(200); + */ + + //pGraphics->GetSceneManager()->GetActiveScene()->mainCam->setPosition(0,1,-20); + + bRunning = true; + float angle = 0.0f; + while(bRunning) + { + angle += 0.001f; + DoLogics(); + DoRendering(); + frame_counter.OnFrameEnd(); + if(frame_counter.GetFrameId() % 500 == 0) + { + printf("Average FPS: %f GUIBatch size=%i\r", frame_counter.GetFpsAvg(), sizeof(GUIVertex)); + } + + tower->getTransform()->SetRotation(Vec3f(0,1,0), angle); + if(frame_counter.GetFrameId() % 100 == 0) + { + //float val = (float)(snd->GetTimePosition() / snd->GetTimeLength()); + //slider->SetValue(val); + //progress->SetValue(val); + //int time = snd->GetTimePosition(); + //char tmp[128]; + + //sprintf(&tmp[0], "Playback time: %i m %i s", time / 60, time % 60); + //time_info->SetText(String(tmp)); + } + } + + DoCleanup(); +} + +void Editor::SetTrackPosition(GUIWindow* sender, int args) +{ + double d = snd->GetTimeLength(); + snd->SeekTime(snd->GetTimeLength() * slider->GetValue()); +} + +void Editor::DoLogics() +{ + pWindow->Update(); + + if(bRunning) + { + sndSystem->Update(); + if(pWindow->IsActive()) + pInput->Capture(); + pGraphics->GetSceneManager()->GetActiveScene()->Update(frame_counter.GetTimeElapled()); + } +} +void Editor::DoRendering() +{ + if(bRunning) + { + pGraphics->GetMainRenderer()->SetDefaultTargets(); + pGraphics->GetMainRenderer()->ClearBuffers(CF_COLOR_TARGET, 0xffffffff, 1.0f, 0); + + pGraphics->GetSceneManager()->GetActiveScene()->mainCam->setPosition(0.0f, 0.6f, -0.5f); + pGraphics->GetSceneManager()->GetActiveScene()->mainCam->setDirection(Vec3f(0,0.3f,0)); + + pGraphics->GetSceneManager()->GetActiveScene()->Render(); + + pGraphics->GetGUI()->Render(); + + /*Video::DebugVertex lines[2]; + lines[0].position = Vec3f(10, 10, 0); + lines[1].position = Vec3f(210, 210, 0); + + lines[0].color = Vec3f(1, 1, 1); + lines[1].color = Vec3f(1, 1, 1); + + pGraphics->GetMainRenderer()->SetScissorRectangle(LifeMath::Rect2i(640, 480));*/ + + /*Video::DebugVertex pts[4]; + pts[0].color = LifeMath::Vec3f(1,0,0); + pts[0].position = LifeMath::Vec3f(10,90,1); + + pts[1].color = LifeMath::Vec3f(0,1,0); + pts[1].position = LifeMath::Vec3f(80,90,1); + + pts[2].color = LifeMath::Vec3f(0,0,1); + pts[2].position = LifeMath::Vec3f(20,310,1); + + pts[3].color = LifeMath::Vec3f(1,1,0); + pts[3].position = LifeMath::Vec3f(90,310,1);*/ + + // for(int i = 0; i < 100; i++) + //pGraphics->debugDrawer->DrawSpline2D(pts, 20); + //pGraphics->debugDrawer->DrawLines2D(lines, 1); + + pGraphics->GetMainRenderer()->SwapBuffers(); + pGraphics->FrameEnd(); + } +} +void Editor::DoCleanup() +{ +} + +bool Editor::KeyDown( Key key, unsigned int text, const std::string& str ) +{ + pGraphics->GetGUI()->KeyDown(key, text, str); + return true; +} +bool Editor::KeyUp( Key key, unsigned int text, const std::string& str ) +{ + return true; +} +bool Editor::MouseMove( int absX, int relX, int absY, int relY) +{ + pGraphics->GetGUI()->MouseMove(absX, relX, absY, relY); + return true; +} +bool Editor::MousePressed(Core::MouseKey key) +{ + pGraphics->GetGUI()->MousePressed(key); + return true; +} +bool Editor::MouseReleased(Core::MouseKey key) +{ + pGraphics->GetGUI()->MouseReleased(key); + return true; +} \ No newline at end of file diff --git a/LifeMaterialEditor/Editor.h b/LifeMaterialEditor/Editor.h new file mode 100644 index 0000000..00b603f --- /dev/null +++ b/LifeMaterialEditor/Editor.h @@ -0,0 +1,88 @@ +#ifndef Editor_h__ +#define Editor_h__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "GUINodeContainer.h" +#include "GUIShaderNode.h" +#include +#include +#include +#include +#include +#include +#include "GUINodeList.h" +#include + +struct NodeInfo +{ + String Name; + String ClassName; + NodeInfo(const String& name, const String& className) + { + Name = name; + ClassName = className; + } +}; + +class Editor : public IKeyListener +{ +public: + Editor(); + ~Editor(); + void Run(); + + bool KeyDown( Key key, unsigned int text, const std::string& str ); + bool KeyUp( Key key, unsigned int text, const std::string& str ); + bool MouseMove( int absX, int relX, int absY, int relY); + bool MousePressed(Core::MouseKey key); + bool MouseReleased(Core::MouseKey key); + +private: + void OnCompileShader(GUIWindow* wnd, int p); + void OnSaveGraph(GUIWindow* wnd, int p); + void OnLoadGraph(GUIWindow* wnd, int p); + void OnGraphNodeDelete(GUINodeContainer* container, GUIShaderNode* node); + + std::vector node_info; + GUINodeList* node_list; + + IShaderGraph* sg; + texture_ptr material_textures[16]; + GUIShaderNode* AddNode(const String& name); + void OnAddNode( GUIWindow* w, const String& s, void* d); + void OnResizeOutput(GUIWindow* wnd); + void menu_exit(GUIMenuItem* m); + GUIProgress* progress; + GUISlider* slider; + GUIText* time_info; + void SwitchPlayback(GUIWindow* wnd, int t); + Sound::SoundSystem* sndSystem; + Sound::ISound* snd; + void AddShaderLink(GUIShaderNodeIO* left, GUIShaderNodeIO* right); + void OnButtonClick(GUIWindow* wnd, int key); + void OnScroll(GUIScroll* wnd, int key); + LifeEngine* pGraphics; + RenderWindowWin* pWindow; + void DoLogics(); + void DoRendering(); + void DoCleanup(); + void SetTrackPosition(GUIWindow* sender, int args); + bool bRunning; + Core::IFileSystem* fileSystem; + FrameCounter frame_counter; + GUIButton* b2; + GUIPicture* sceneOutput; + GUINodeContainer* nodeContainer; + InputSystemOIS* pInput; + Video::IObjectMesh* tower; + GUIPropertyGrid* propGrid; +}; +#endif // Editor_h__ \ No newline at end of file diff --git a/LifeMaterialEditor/EditorShared.h b/LifeMaterialEditor/EditorShared.h new file mode 100644 index 0000000..75cb3a2 --- /dev/null +++ b/LifeMaterialEditor/EditorShared.h @@ -0,0 +1,13 @@ +#ifndef EditorShared_h__ +#define EditorShared_h__ + +#include +#include + +class EditorShared; +class EditorShared : public Singleton +{ +public: + GUIPropertyGrid* propGrid; +}; +#endif // EditorShared_h__ \ No newline at end of file diff --git a/LifeMaterialEditor/GUINodeContainer.cpp b/LifeMaterialEditor/GUINodeContainer.cpp new file mode 100644 index 0000000..dbd6843 --- /dev/null +++ b/LifeMaterialEditor/GUINodeContainer.cpp @@ -0,0 +1,531 @@ +#include "GUINodeContainer.h" +#include +#include +#include +#include +#include "GUIShaderNodeTexture.h" +#include "EditorShared.h" + +GUINodeContainer::GUINodeContainer(IShaderGraph* graph) + :cache_length(0) +{ + graphics_graph = graph; + //skip_mouse = true; + menu = new GUIMenu(true); +} + +GUIShaderNode* GUINodeContainer::FindNodeByGraphicsID(int id) +{ + int node_count = GetNodeCount(); + for(int i = 0; i < node_count; i++){ + GUIShaderNode* node = nodes[i]; + if(node->GetGraphicsNode()->id == id) + return node; + } + return nullptr; +} +bool GUINodeContainer::Serialize(Core::IMemoryStream* stm) +{ + int node_count = GetNodeCount(); + stm->Write(node_count); + + for(int i = 0; i < node_count; i++){ + GUIShaderNode* node = nodes[i]; + stm->Write(node->GetGraphicsNode()->id); + stm->Write(node->GetPosition().X); + stm->Write(node->GetPosition().Y); + stm->Write(node->GetWidth()); + stm->Write(node->GetHeight()); + StringSerialize(node->GetCaption(), stm); + } + return true; +} +bool GUINodeContainer::Deserialize(Core::IMemoryStream* stm) +{ + int node_count; + stm->ReadInt32(node_count); + + for(int i = 0; i < node_count; i++){ + int graphics_id; + int pos_x, pos_y; + int node_width, node_height; + String caption; + GUIShaderNode* node; + + stm->ReadInt32(graphics_id); + stm->ReadInt32(pos_x); + stm->ReadInt32(pos_y); + stm->ReadInt32(node_width); + stm->ReadInt32(node_height); + caption = StringDeserialize(stm); + + node = FindNodeByGraphicsID(graphics_id); + + if(node != nullptr){ + node->SetPosition(Vec2i(pos_x, pos_y)); + node->SetWidth(node_width); + node->SetHeight(node_height); + node->SetCaption(caption); + } + } + return true; +} +void GUINodeContainer::MakeLink(GUIShaderNodeIO* left, GUIShaderNodeIO* right) +{ + LinkInfo info; + info.left = left; + info.right = right; + links.push_back(info); + Invalidate(); +} +void GUINodeContainer::BreakLink(GUIShaderNodeIO* left, GUIShaderNodeIO* right) +{ + for(LinksCollectionItr it = links.begin(); it != links.end(); ++it) + { + LinkInfo info = *it; + if((info.left == left && info.right == right) || + (info.left == right && info.right == left)) + { + links.erase(it); + return; + } + } +} + +void GUINodeContainer::OnNodeSelect(GUIShaderNode* node) +{ + EditorShared::me()->propGrid->SetTarget(node); +} +NodeCollection* GUINodeContainer::GetTextureUnits() +{ + return &texNodes; +} + void GUINodeContainer::SetSelection(GUIShaderNode* node) + { + selection = node; + } + void GUINodeContainer::OnNodeHeaderMouseClick(GUIWindow* wnd, int param) + { + if(param == KEY_MOUSE_RIGHT) + { + this->SetSelection((GUIShaderNode*)wnd); + menu->RemoveItems(); + menu->AddItem(STR("Delete node"), false)->Event_Select += NewDelegate(this, &GUINodeContainer::OnDeleteNode); + menu->SetPosition(FindOverlay()->cursorPos); + + + FindOverlay()->AddChild(menu); + FindOverlay()->SetFocusWindow(menu); + } + } + + void GUINodeContainer::OnDeleteNode(GUIMenuItem* itm) + { + DeleteNode(selection); + } + void GUINodeContainer::DeleteNode(GUIShaderNode* node) + { + Event_DeleteNode.Invoke(this, node); + this->RemoveChild(node->hwnd); + graphics_graph->DeleteNode(node->GetGraphicsNode()->id); + + + restart_delete_links: + for(LinksCollectionItr it = links.begin(); it != links.end(); ++it) + { + if((*it).left->Owner == node || (*it).right->Owner == node) + { + links.erase(it); + goto restart_delete_links; + } + } + + + + for(NodeCollectionItr it = nodes.begin(); it != nodes.end(); it++) + { + if((*it)->ID == node->ID) + { + nodes.erase(it); + goto done; + } + } + done: + delete node; + Invalidate(); + } + void GUINodeContainer::RegisterNewNode(GUIShaderNode* node) + { + nodes.push_back(node); + node->Event_HeaderClick += NewDelegate(this, &GUINodeContainer::OnNodeHeaderMouseClick); + } +void GUINodeContainer::RebuildGraph() +{ + //remove all nodes firstly + for(NodeCollectionItr it = nodes.begin(); it != nodes.end(); it++) + { + //Event_DeleteNode.Invoke(this, node); + this->RemoveChild((*it)->hwnd); + delete (*it); + } + nodes.clear(); + + links.clear(); + + + int cnt = graphics_graph->GetNodeCount(); + + //build nodes + for(int k = 0; k < cnt; k++) + { + IShaderGraphNode* gn = graphics_graph->GetNodeByArrayID(k); + GUIShaderNode* node; + + if(gn->Name == STR("SrcTex2D")){ + node = new GUIShaderNodeTexture(gn); + texNodes.push_back(node); + } + else + node = new GUIShaderNode(gn); + + //copy all properties =) + PropertyCollection* props = gn->GetProperties(); + for(int i = 0; i < props->size(); i++) + { + IProperty* prop = (*props)[i]; + prop->Event_ValueChanged.Invoke(); + node->RegisterProperty(prop); + } + + node->Event_Select += NewDelegate(this, &GUINodeContainer::OnNodeSelect); + + node->SetCaption(gn->Name); + int in_count = gn->Inputs.size(); + int out_count = gn->Outputs.size(); + + //create input ports + for(int i = 0; i < in_count; i++) + { + IShaderGraphNodePort* port = gn->Inputs[i]; + GUIShaderNodeIO* io = new GUIShaderNodeIO(node); + io->SetCaption(port->Name); + node->AddInput(io); + io->GraphicsPort = port; + io->Event_PortClick += NewDelegate(this, &GUINodeContainer::OnNodePortMouseClick); + } + + //create output ports + for(int i = 0; i < out_count; i++) + { + IShaderGraphNodePort* port = gn->Outputs[i]; + GUIShaderNodeIO* io = new GUIShaderNodeIO(node); + io->SetCaption(port->Name); + io->GraphicsPort = port; + io->Event_PortClick += NewDelegate(this, &GUINodeContainer::OnNodePortMouseClick); + node->AddOutput(io); + } + + if(name == STR("Material")) + { + materialNode = node; + } + AddChild(node); + RegisterNewNode(node); + } + + + + + + //build links + cnt = nodes.size(); + for(int k = 0; k < cnt; k++) + { + GUIShaderNode* node = nodes[k]; + IShaderGraphNode* n = node->GetGraphicsNode(); + int inc = n->Inputs.size(); + + for(int c = 0; c < inc; c++) + { + //make link + if(n->Inputs[c]->GetLinkCount()) + { + IShaderGraphNodePort* right_port = n->Inputs[c]->GetLink(0); + //get right node + GUIShaderNode* rn = this->FindNode(right_port->Parent->id); + + int roc = rn->GetOutputCount(); + + int rightId = -1; + for(int m = 0; m < roc; m++) + { + if(rn->GetGraphicsNode()->GetOutputPortByID(m) == right_port) + { + rightId = m; + } + } + + + //found right side link - make it! + if(rightId >=0) + { + MakeLink(node->GetInput(c), rn->GetOutput(rightId)); + } + } + } + } +} +String GUINodeContainer::GetTexNameByID(int id) +{ + int cnt = nodes.size(); + for(int i = 0; i < cnt; i++) + { + if(nodes[i]->GetGraphicsNode()->id == id) + { + GUIShaderNodeTexture* tn = (GUIShaderNodeTexture*)nodes[i]; + return tn->GetFilename(); + } + } + return "TexNotFound"; +} +GUIShaderNode* GUINodeContainer::FindNode(int id) +{ + int cnt = nodes.size(); + for(int k = 0; k < cnt; k++) + { + GUIShaderNode* node = nodes[k]; + if(node->GetGraphicsNode()->id == id) + return node; + } + return nullptr; +} +GUIShaderNode* GUINodeContainer::AddNode(const String& name) +{ + IShaderGraphNode* gn = graphics_graph->AddNode(name); + GUIShaderNode* node; + + if(name == STR("SrcTex2D")) + { + node = new GUIShaderNodeTexture(gn); + texNodes.push_back(node); + } + else + { + node = new GUIShaderNode(gn); + } + + //copy all properties =) + PropertyCollection* props = gn->GetProperties(); + for(int i = 0; i < props->size(); i++) + { + IProperty* prop = (*props)[i]; + node->RegisterProperty(prop); + } + + node->Event_Select += NewDelegate(this, &GUINodeContainer::OnNodeSelect); + + int in_count = gn->Inputs.size(); + int out_count = gn->Outputs.size(); + + //create input ports + for(int i = 0; i < in_count; i++) + { + IShaderGraphNodePort* port = gn->Inputs[i]; + GUIShaderNodeIO* io = new GUIShaderNodeIO(node); + io->SetCaption(port->Name); + node->AddInput(io); + io->GraphicsPort = port; + io->Event_PortClick += NewDelegate(this, &GUINodeContainer::OnNodePortMouseClick); + } + + //create output ports + for(int i = 0; i < out_count; i++) + { + IShaderGraphNodePort* port = gn->Outputs[i]; + GUIShaderNodeIO* io = new GUIShaderNodeIO(node); + io->SetCaption(port->Name); + node->AddOutput(io); + io->GraphicsPort = port; + io->Event_PortClick += NewDelegate(this, &GUINodeContainer::OnNodePortMouseClick); + } + + if(name == STR("Material")) + { + materialNode = node; + } + AddChild(node); + RegisterNewNode(node); + return node; +} + +void GUINodeContainer::OnDeleteLink(GUIMenuItem* itm) +{ + selected_port->GraphicsPort->BreakLink(); + +restart_delete_links: + for(LinksCollectionItr it = links.begin(); it != links.end(); ++it) + { + if((*it).left == selected_port || (*it).right == selected_port) + { + links.erase(it); + goto restart_delete_links; + } + } + Invalidate(); +} +void GUINodeContainer::OnNodePortMouseClick(GUIWindow* wnd, int param) +{ + //right click on node IO port + if(param == KEY_MOUSE_RIGHT) + { + menu->RemoveItems(); + selected_port = (GUIShaderNodeIO*)wnd; + menu->AddItem("Delete links", false)->Event_Select += NewDelegate(this, &GUINodeContainer::OnDeleteLink); + menu->SetPosition(FindOverlay()->cursorPos); + FindOverlay()->AddChild(menu); + FindOverlay()->SetFocusWindow(menu); + } +} +void GUINodeContainer::OnNodeLayoutUpdate() +{ + Invalidate(); +} +GUINodeContainer::~GUINodeContainer() +{ +} +void GUINodeContainer::SetBackColor(const Vec4f& color) +{ +} +void GUINodeContainer::GenGeom() +{ + cache_length = 0; + + int lc = links.size(); + + for(int i = 0; i < lc; i++) + { + LinkInfo info = links[i]; + Vec2i p1 = info.left->GetAttachPoint(); + Vec2i p2; + Vec2i p3; + Vec2i p4 = info.right->GetAttachPoint(); + + //if input - shift to right + if(info.left->IsInput()) + { + p2 = p1 + Vec2i(50, 0); + p3 = p4 + Vec2i(-50, 0); + } + else + { + p2 = p1 + Vec2i(-50, 0); + p3 = p4 + Vec2i(50, 0); + } + + GUIVertex points[4]; + points[0].Color = Vec4f(1,1,1,1); + points[1].Color = Vec4f(1,1,1,1); + points[2].Color = Vec4f(1,1,1,1); + points[3].Color = Vec4f(1,1,1,1); + + points[0].Position = Vec3f((float)p1.X, (float)p1.Y, 0); + points[1].Position = Vec3f((float)p2.X, (float)p2.Y, 0); + points[2].Position = Vec3f((float)p3.X, (float)p3.Y, 0); + points[3].Position = Vec3f((float)p4.X, (float)p4.Y, 0); + + //we have 4 points! now we have to convert them to cubic spline + int offset = 0; + GUIVertex prev; + prev = points[0]; + for(int i = 1; i <= SPLINE_FREQ; i++) + { + GUIVertex newP = Core::CubicBezier((1.0f / (float)SPLINE_FREQ) * (float)i, points[0], points[1], points[2], points[3]); + cache[offset++] = prev; + cache[offset++] = newP; + + prev = newP; + } + + offset = 0; + + Rect2i sc = GetScissor(); + + GUIVertex tmp; + //all spline points generated! Now it's time to cut them by owner's client rectangle + for(int i = 0; i < SPLINE_FREQ; i++) + { + GUIVertex v1 = cache[i * 2]; + GUIVertex v2 = cache[i * 2 + 1]; + + //test is vertices are visible + if(v1.Position.X < sc.x && v2.Position.X < sc.x) continue; + if(v1.Position.X > sc.GetMaxX() && v2.Position.X > sc.GetMaxX()) continue; + if(v1.Position.Y < sc.y && v2.Position.Y < sc.y) continue; + if(v1.Position.Y > sc.GetMaxY() && v2.Position.Y > sc.GetMaxY()) continue; + + //sort vertices + if(v1.Position.X > v2.Position.X) + { + tmp = v1; + v1 = v2; + v2 = tmp; + } + //crop right side + if(v2.Position.X > sc.GetMaxX()) + { + float slope = (v2.Position.Y - v1.Position.Y) / (v2.Position.X - v1.Position.X); + float xd = v2.Position.X - float(sc.GetMaxX()); + v2.Position.X -= xd; + v2.Position.Y -= xd * slope; + } + //crop left side + if(v1.Position.X < sc.x) + { + float slope = (v2.Position.Y - v1.Position.Y) / (v2.Position.X - v1.Position.X); + float xd = sc.x - v1.Position.X; + v1.Position.X += xd; + v1.Position.Y += xd * slope; + } + + //sort vertices + if(v1.Position.Y > v2.Position.Y) + { + tmp = v1; + v1 = v2; + v2 = tmp; + } + //crop bottom side + if(v2.Position.Y > sc.GetMaxY()) + { + float slope = (v2.Position.X - v1.Position.X) / (v2.Position.Y - v1.Position.Y); + float yd = v2.Position.Y - float(sc.GetMaxY()); + v2.Position.X -= yd * slope; + v2.Position.Y -= yd; + } + //crop top side + if(v1.Position.Y < sc.y) + { + float slope = (v2.Position.X - v1.Position.X) / (v2.Position.Y - v1.Position.Y); + float yd = sc.y - v1.Position.Y; + v1.Position.X += yd * slope; + v1.Position.Y += yd; + } + + cache2[cache_length++] = v1; + cache2[cache_length++] = v2; + } + } + invalid = false; +} +void GUINodeContainer::RenderSelf() +{ + if(invalid) + GenGeom(); + + if(cache_length) + { + Video::IGUIRenderer* renderer = GUIManager::me()->GetRenderer(); + renderer->SetTexture(GUIManager::me()->DIFF, 0, true); + renderer->Write(&cache2[0], cache_length); + } +} \ No newline at end of file diff --git a/LifeMaterialEditor/GUINodeContainer.h b/LifeMaterialEditor/GUINodeContainer.h new file mode 100644 index 0000000..b0ed49d --- /dev/null +++ b/LifeMaterialEditor/GUINodeContainer.h @@ -0,0 +1,95 @@ +#ifndef GUINodeContainer_h__ +#define GUINodeContainer_h__ + +#include +#include +#include +#include "GUIShaderNode.h" +#include "GUIShaderNodeTexture.h" +#include +#include +#include +#include + +struct LinkInfo +{ + GUIShaderNodeIO* left; + GUIShaderNodeIO* right; +}; + +#define SPLINE_FREQ 20 +typedef std::vector NodeCollection; +typedef std::vector::iterator NodeCollectionItr; + +class GUINodeContainer : public GUIWindow, public ISerializable +{ +public: + EVENT2(GUINodeContainer_NodeEvent, GUINodeContainer*, GUIShaderNode*); + GUINodeContainer_NodeEvent Event_DeleteNode; + + GUINodeContainer(IShaderGraph* graph); + ~GUINodeContainer(); + bool Serialize(Core::IMemoryStream* stm); + bool Deserialize(Core::IMemoryStream* stm); + + //void AddChild(GUIWindow* val); + void MakeLink(GUIShaderNodeIO* left, GUIShaderNodeIO* right); + void BreakLink(GUIShaderNodeIO* left, GUIShaderNodeIO* right); + + void SetBackColor(const Vec4f& color); + void RenderSelf(); + GUIShaderNode* FindNode(int id); + GUIShaderNode* AddNode(const String& name); + void Invalidate() + { + GUIWindow::Invalidate(); + invalid = true; + } + GUIShaderNode* GetMaterialNode() + { + return materialNode; + } + NodeCollection* GetTextureUnits(); + void RebuildGraph(); + size_t GetNodeCount() + { + return nodes.size(); + } + GUIShaderNode* getNode(int id) + { + return nodes[id]; + } + String GetTexNameByID(int id); +private: + void OnNodeSelect(GUIShaderNode* node); + void GenGeom(); + void OnNodeLayoutUpdate(); + void SetSelection(GUIShaderNode* node); + void DeleteNode(GUIShaderNode* node); + void OnNodeHeaderMouseClick(GUIWindow* wnd, int param); + void OnNodePortMouseClick(GUIWindow* wnd, int param); + + + void OnDeleteLink(GUIMenuItem* itm); + + void OnDeleteNode(GUIMenuItem* itm); + + void RegisterNewNode(GUIShaderNode* node); + + GUIShaderNode* FindNodeByGraphicsID(int id); + GUIShaderNode* selection; + GUIShaderNodeIO* selected_port; + GUIMenu* menu; + NodeCollection nodes; + NodeCollection texNodes; + GUIShaderNode* materialNode; + bool invalid; + IShaderGraph* graphics_graph; + typedef std::vector LinksCollection; + typedef std::vector::iterator LinksCollectionItr; + LinksCollection links; + int cache_length; + GUIVertex cache[SPLINE_FREQ * 2]; + GUIVertex cache2[SPLINE_FREQ * 2 * 128]; //128 splines cache +}; +#endif // GUINodeContainer_h__ \ No newline at end of file diff --git a/LifeMaterialEditor/GUINodeList.cpp b/LifeMaterialEditor/GUINodeList.cpp new file mode 100644 index 0000000..e69de29 diff --git a/LifeMaterialEditor/GUINodeList.h b/LifeMaterialEditor/GUINodeList.h new file mode 100644 index 0000000..9d2dd10 --- /dev/null +++ b/LifeMaterialEditor/GUINodeList.h @@ -0,0 +1,11 @@ +#ifndef GUINodeList_h__ +#define GUINodeList_h__ + +#include +#include +#include + +class GUINodeList : public GUIList +{ +}; +#endif // GUINodeList_h__ \ No newline at end of file diff --git a/LifeMaterialEditor/GUINodePort.cpp b/LifeMaterialEditor/GUINodePort.cpp new file mode 100644 index 0000000..993e260 --- /dev/null +++ b/LifeMaterialEditor/GUINodePort.cpp @@ -0,0 +1,52 @@ +#include "GUINodePort.h" +#include + +GUINodePort::GUINodePort() + :input_mode(false) +{ + allowDrop = true; + allowDrag = true; + can_drag = false; +} +GUINodePort::~GUINodePort() +{ +} +void GUINodePort::FinishDrag() +{ +} +bool GUINodePort::DragEnter(DragInfo* info) +{ + if(this->input_mode == true && info->Name == STR("Shader_Output")) + return true; + if(this->input_mode == false && info->Name == STR("Shader_Input")) + return true; + return false; +} +void GUINodePort::AcceptDrop(DragInfo* info) +{ + Event_Link.Invoke(this, reinterpret_cast(info->data)); +} +void GUINodePort::OnMouseMove(int abs_x, int abs_y, int rel_x, int rel_y) +{ + if(can_drag) + { + can_drag = false; + if(input_mode) + info.Name = STR("Shader_Input"); + else + info.Name = STR("Shader_Output"); + + info.data = this; + FindOverlay()->BeginDrag(this, &info); + } + else + { + GUIPlane::OnMouseMove(abs_x, abs_y, rel_x, rel_y); + } +} + +void GUINodePort::OnMousePress(int x, int y, int key) +{ + can_drag = true; + GUIPlane::OnMousePress(x, y, key); +} \ No newline at end of file diff --git a/LifeMaterialEditor/GUINodePort.h b/LifeMaterialEditor/GUINodePort.h new file mode 100644 index 0000000..d9006f6 --- /dev/null +++ b/LifeMaterialEditor/GUINodePort.h @@ -0,0 +1,29 @@ +#ifndef GUINodePort_h__ +#define GUINodePort_h__ + +#include + +class GUINodePort : public Video::GUIPlane +{ +public: + EVENT2(PortEvent, GUINodePort*, GUINodePort*); + PortEvent Event_Link; + GUINodePort(); + ~GUINodePort(); + void FinishDrag(); + bool DragEnter(DragInfo* info); + void AcceptDrop(DragInfo* info); + void OnMouseMove(int abs_x, int abs_y, int rel_x, int rel_y); + void OnMousePress(int x, int y, int key); + void SetMode(bool isInput) + { + input_mode = isInput; + } + void* user_data; +private: + + bool input_mode; + bool can_drag; + DragInfo info; +}; +#endif // GUINodePort_h__ \ No newline at end of file diff --git a/LifeMaterialEditor/GUIShaderNode.cpp b/LifeMaterialEditor/GUIShaderNode.cpp new file mode 100644 index 0000000..fc283f6 --- /dev/null +++ b/LifeMaterialEditor/GUIShaderNode.cpp @@ -0,0 +1,318 @@ +#include "GUIShaderNode.h" + +GUIShaderNode::GUIShaderNode(IShaderGraphNode* src) + :resizing(false), moving(false), GraphicsNode(src) +{ + skip_mouse = true; + size_min = Vec2i(100, 50); + size_max = Vec2i(250, 250); + + header = new GUIPlane(); + header->SetPosition(Vec2i(0, 0)); + header->SetWidth(120); + header->SetHeight(25); + header->SetBackColor(Vec4f(0.16f,0.16f,0.16f,1)); + header->SetBorderColor(Vec4f(0.016f,0.016f,0.016f,1)); + + body = new GUIPlane(); + body->SetPosition(Vec2i(0, 25)); + body->SetWidth(120); + body->SetHeight(120); + body->SetBackColor(Vec4f(0.25f,0.0f,0.0f,1)); + body->SetBorderColor(Vec4f(0.016f,0.016f,0.016f,1)); + + display = new GUIPicture(); + body->AddChild(display); + display->SetPosition(Vec2i(2,2)); + back = new GUIPlane(); + back->SetPosition(Vec2i(0, 0)); + back->SetWidth(width); + back->SetHeight(height); + back->SetBackColor(Vec4f(0.9f,0.25f,0.25f,1)); + back->SetBorderColor(Vec4f(0.9f,0.016f,0.016f,1)); + + mover = new GUIPlane(); + mover->SetPosition(Vec2i(115, 140)); + mover->SetWidth(10); + mover->SetHeight(10); + mover->SetBackColor(Vec4f(0.75f,0.75f,0.75f,0.5f)); + mover->SetBorderColor(Vec4f(0.016f,0.016f,0.016f,1)); + mover->cursor = STR("Size_d1"); + + header_text = new GUIText(); + header->AddChild(header_text); + header_text->SetDock(Video::DOCK_FILL); + header_text->SetHAlign(HA_CENTER); + header_text->SetVAlign(VA_CENTER); + header_text->SetText("Invert (1-x)"); + header_text->SetBackColor(Vec4f(0.0f,0.0f,0.0f,0.0f)); + header_text->SetForeColor(Vec4f(0.8f,0.8f,0.8f,1.0f)); + header_text->mouseEnabled = false; + + mover->Event_MouseDown += NewDelegate(this, &GUIShaderNode::OnBeginResize); + mover->Event_MouseUp += NewDelegate(this, &GUIShaderNode::OnEndResize); + mover->Event_MouseMove += NewDelegate(this, &GUIShaderNode::OnDoResize); + + header->Event_MouseDown += NewDelegate(this, &GUIShaderNode::OnHeaderMouseDown); + header->Event_MouseUp += NewDelegate(this, &GUIShaderNode::OnHeaderMouseUp); + header->Event_MouseMove += NewDelegate(this, &GUIShaderNode::OnHeaderMouseMove); + header->Event_MouseClick += NewDelegate(this, &GUIShaderNode::OnHeaderMouseClick); + + menu = new GUIMenu(true); + menu->AddItem("Delete node", false); + + //AddChild(back); + AddChild(body); + AddChild(mover); + AddChild(header); +} +void GUIShaderNode::OnHeaderRightClick(GUIWindow* wnd, int param) +{ + printf("Right click on header!\n"); +} + +void GUIShaderNode::OnHeaderMouseClick(GUIWindow* wnd, int param) +{ + Event_HeaderClick.Invoke(this, param); +} +void GUIShaderNode::SetDisplayImage(texture_ptr tex) +{ + displayImage = tex; + display->SetPicture(CImage::FromTexture(displayImage.ptr())); +} +void GUIShaderNode::SetCaption(const String& text) +{ + header_text->SetText(text); +} +const String& GUIShaderNode::GetCaption() +{ + return header_text->GetText(); +} +GUIShaderNode::~GUIShaderNode() +{ +} +void GUIShaderNode::OnBeginResize(GUIWindow* wnd, int param) +{ + resizing = true; + FindOverlay()->GrabMouse(wnd); + size_pos = body->GetRectangleAbs().GetMax() - FindOverlay()->cursorPos; +} +void GUIShaderNode::OnDoResize(GUIWindow* wnd, int param) +{ + if(resizing) + { + Vec2i newPos = FindOverlay()->cursorPos + size_pos; + Vec2i diff = newPos - body->GetRectangleAbs().GetMin(); + + if(diff.X < size_min.X) + diff.X = size_min.X; + if(diff.Y < size_min.Y) + diff.Y = size_min.Y; + + if(diff.X > size_max.X) + diff.X = size_max.X; + if(diff.Y > size_max.Y) + diff.Y = size_max.Y; + + body->SetWidth(diff.X); + body->SetHeight(diff.Y); + header->SetWidth(diff.X); + + display->SetWidth(body->GetClientRectangle().width - 4); + display->SetHeight(body->GetClientRectangle().height - 4); + + Invalidate(); + } +} +void GUIShaderNode::OnEndResize(GUIWindow* wnd, int param) +{ + resizing = false; + FindOverlay()->ReleaseMouse(); +} + +void GUIShaderNode::OnHeaderMouseDown(GUIWindow* wnd, int param) +{ + if(param == KEY_MOUSE_LEFT) + { + OnSelect(); + moving = true; + FindOverlay()->GrabMouse(wnd); + size_pos = this->GetRectangleAbs().GetMin() - FindOverlay()->cursorPos; + } + +} +void GUIShaderNode::OnHeaderMouseMove(GUIWindow* wnd, int param) +{ + if(moving) + { + Vec2i new_pos = parent->CursorToLocal(FindOverlay()->cursorPos + size_pos); + this->SetPosition(new_pos); + Invalidate(); + } +} +void GUIShaderNode::OnHeaderMouseUp(GUIWindow* wnd, int param) +{ + if(param == KEY_MOUSE_LEFT) + { + FindOverlay()->ReleaseMouse(); + moving = false; + } +} +void GUIShaderNode::ProcessLink(GUIShaderNodeIO* my, GUIShaderNodeIO* other) +{ + if(my->Owner != other->Owner) + { + Event_Attach.Invoke(my, other); + } +} +void GUIShaderNode::FormatInputs() +{ + int cnt = inputs.size(); + int spacing = 5; + + if(!cnt) return; + + int totalSize = spacing * (cnt - 1) + inputs[0]->GetHeight() * cnt; + + int step = totalSize / cnt; + + int h = body->GetHeight(); + int start = (h - totalSize) / 2 + body->GetPosition().Y; + + for(int i = 0; i < cnt; i++) + { + Vec2i pos = inputs[i]->GetPosition(); + int newX = body->GetRectangle().GetMaxX(); + if(pos.X != newX) + Invalidate(); + pos.X = newX; + + pos.Y = start + (step * i); + inputs[i]->SetPosition(pos); + } +} +void GUIShaderNode::FormatOutputs() +{ + int cnt = outputs.size(); + int spacing = 5; + + if(!cnt) return; + + int totalSize = spacing * (cnt - 1) + outputs[0]->GetHeight() * cnt; + + int step = totalSize / cnt; + + int h = body->GetHeight(); + int start = (h - totalSize) / 2 + body->GetPosition().Y; + + for(int i = 0; i < cnt; i++) + { + Vec2i pos = outputs[i]->GetPosition(); + int newX = body->GetPosition().X - outputs[i]->GetWidth(); + if(pos.X != newX) + Invalidate(); + pos.X = newX; + pos.Y = start + (step * i); + outputs[i]->SetPosition(pos); + } +} +void GUIShaderNode::AddInput(GUIShaderNodeIO* node) +{ + node->SetMode(true); + node->Event_Link += NewDelegate(this, &GUIShaderNode::ProcessLink); + node->Event_Resize += NewDelegate(this, &GUIShaderNode::OnIOResize); + node->id = inputs.size(); + inputs.push_back(node); + node->Owner = this; + AddChild(node); + FormatInputs(); +} +void GUIShaderNode::OnIOResize(GUIWindow* wnd) +{ + this->Invalidate(); +} +void GUIShaderNode::AddOutput(GUIShaderNodeIO* node) +{ + node->SetMode(false); + node->Event_Link += NewDelegate(this, &GUIShaderNode::ProcessLink); + node->Event_Resize += NewDelegate(this, &GUIShaderNode::OnIOResize); + AddChild(node); + node->Owner = this; + node->id = outputs.size(); + outputs.push_back(node); + FormatOutputs(); +} +size_t GUIShaderNode::GetInputCount() +{ + return inputs.size(); +} +size_t GUIShaderNode::GetOutputCount() +{ + return outputs.size(); +} +GUIShaderNodeIO* GUIShaderNode::GetInput(int id) +{ + return inputs[id]; +} +GUIShaderNodeIO* GUIShaderNode::GetOutput(int id) +{ + return outputs[id]; +} + + +void GUIShaderNode::OnSelect() +{ + Event_Select.Invoke(this); +} +void GUIShaderNode::RenderSelf() +{ + if(invalid) + { + LifeMath::Rect2i tr = childs[0]->GetRectangle(); + + //find all childs bounding rectangle + for(WindowList::iterator it = childs.begin(); it!= childs.end(); ++it) + { + GUIWindow* w = (*it); + tr.Merge(w->GetRectangle()); + } + + int new_width = tr.width; + int new_height = tr.height; + + if(this->width != new_width) + this->SetWidth(new_width); + + if(this->height != new_height) + this->SetHeight(new_height); + + mover->SetPosition(body->GetRectangle().GetMax() - Vec2i(5,5)); + + //need to move all childs + if(tr.x !=0 || tr.y !=0) + { + for(WindowList::iterator it = childs.begin(); it!= childs.end(); ++it) + { + GUIWindow* w = (*it); + Vec2i pp = w->GetPosition(); + w->SetPosition(Vec2i(pp.X - tr.x, pp.Y - tr.y)); + } + } + FormatInputs(); + FormatOutputs(); + invalid = false; + + if(parent) + parent->Invalidate(); + } +} +void GUIShaderNode::Update(float dt) +{ +} +void GUIShaderNode::SetBackColor(const Vec4f& color) +{ +} +bool GUIShaderNode::ProcessMessage(GUIMessageType mt, GUIMessageParam mp, IMessageArgs* args) +{ + return false; +} \ No newline at end of file diff --git a/LifeMaterialEditor/GUIShaderNode.h b/LifeMaterialEditor/GUIShaderNode.h new file mode 100644 index 0000000..1e0a75f --- /dev/null +++ b/LifeMaterialEditor/GUIShaderNode.h @@ -0,0 +1,96 @@ +#ifndef GUIShaderNode_h__ +#define GUIShaderNode_h__ +#include +#include +#include +#include +#include +#include "GUIShaderNodeIO.h" +#include +#include +#include +#include + +class GUIShaderNode : public GUIWindow, public Core::IPropertyHolder +{ +public: + + EVENT(LayoutEvent); + LayoutEvent Event_LayoutChanged; + + EVENT1(ShaderNodeEvent, GUIShaderNode*); + ShaderNodeEvent Event_Select; + + EVENT2(ShaderNodeNouseEvent, GUIShaderNode*, int); + ShaderNodeNouseEvent Event_HeaderClick; + + EVENT2(NodeEvent, GUIShaderNodeIO*, GUIShaderNodeIO*); + NodeEvent Event_Attach; + GUIShaderNode(IShaderGraphNode* src); + ~GUIShaderNode(); + void SetCaption(const String& text); + const String& GetCaption(); + void Update(float dt); + void RenderSelf(); + void SetBackColor(const Vec4f& color); + void AddInput(GUIShaderNodeIO* node); + void AddOutput(GUIShaderNodeIO* node); + size_t GetInputCount(); + size_t GetOutputCount(); + GUIShaderNodeIO* GetInput(int id); + GUIShaderNodeIO* GetOutput(int id); + + bool ProcessMessage(GUIMessageType mt, GUIMessageParam mp, IMessageArgs* args); + void* UserData; + + IShaderGraphNode* GetGraphicsNode() + { + return GraphicsNode; + } + void Invalidate() + { + GUIWindow::Invalidate(); + invalid = true; + } +private: + GUIMenu* menu; + IShaderGraphNode* GraphicsNode; + bool invalid; + GUIPicture* display; + + bool resizing; + bool moving; + Vec2i size_pos; + Vec2i size_min; + Vec2i size_max; + + void OnHeaderRightClick(GUIWindow* wnd, int param); + void OnBeginResize(GUIWindow* wnd, int param); + void OnDoResize(GUIWindow* wnd, int param); + void OnEndResize(GUIWindow* wnd, int param); + + void OnHeaderMouseDown(GUIWindow* wnd, int param); + void OnHeaderMouseMove(GUIWindow* wnd, int param); + void OnHeaderMouseUp(GUIWindow* wnd, int param); + void OnHeaderMouseClick(GUIWindow* wnd, int param); + + void OnSelect(); + void ProcessLink(GUIShaderNodeIO* my, GUIShaderNodeIO* other); + void FormatInputs(); + void FormatOutputs(); + void OnIOResize(GUIWindow* wnd); + GUIPlane* header; + GUIText* header_text; + GUIPlane* body; + GUIPlane* mover; + GUIPlane* back; + typedef std::vector IoVector; + IoVector inputs; + IoVector outputs; + +protected: + void SetDisplayImage(texture_ptr tex); + texture_ptr displayImage; +}; + +#endif // GUIShaderNode_h__ \ No newline at end of file diff --git a/LifeMaterialEditor/GUIShaderNodeIO.cpp b/LifeMaterialEditor/GUIShaderNodeIO.cpp new file mode 100644 index 0000000..cfb57ee --- /dev/null +++ b/LifeMaterialEditor/GUIShaderNodeIO.cpp @@ -0,0 +1,98 @@ +#include "GUIShaderNodeIO.h" +#include + +GUIShaderNodeIO::GUIShaderNodeIO(GUIShaderNode* _parent) + :mode_input(true), can_drag(false) +{ + Owner = _parent; + skip_mouse = true; + arrow = new GUINodePort(); + arrow->SetWidth(14); + arrow->SetHeight(10); + arrow->SetBackColor(Vec4f(0,0,1,1)); + arrow->user_data = this; + arrow->SetMode(mode_input); + AddChild(arrow); + + caption = new GUIText(); + caption->SetAutoSize(true); + caption->SetText("UV"); + caption->SetBackColor(Vec4f(1,1,1,0.1f)); + caption->SetForeColor(Vec4f(0,0,0,1.0f)); + AddChild(caption); + + arrow->Event_MouseDown += NewDelegate(this, &GUIShaderNodeIO::OnArrowPress); + arrow->Event_MouseClick += NewDelegate(this, &GUIShaderNodeIO::OnArrowClick); + arrow->Event_Link += NewDelegate(this, &GUIShaderNodeIO::AcceptLink); +} +Vec2i GUIShaderNodeIO::GetAttachPoint() +{ + Vec2i pos = arrow->GetPositionAbs(); + pos.Y += arrow->GetHeight() / 2; + + if(IsInput()) + { + pos.X += arrow->GetWidth(); + } + return pos; +} +GUIShaderNodeIO::~GUIShaderNodeIO() +{ +} +void GUIShaderNodeIO::AcceptLink(GUINodePort* me, GUINodePort* other) +{ + Event_Link.Invoke(this, reinterpret_cast(other->user_data)); +} +void GUIShaderNodeIO::SetCaption(const String& _caption) +{ + caption->SetText(_caption); +} +void GUIShaderNodeIO::SetMode(bool input) +{ + mode_input = input; + arrow->SetMode(mode_input); +} +void GUIShaderNodeIO::Update(float dt) +{ +} +void GUIShaderNodeIO::RenderSelf() +{ + int caption_offset = 3; + + if(this->GetWidth() != (caption->GetWidth() + arrow->GetWidth() + caption_offset)) + { + this->SetWidth(caption->GetWidth() + arrow->GetWidth() + caption_offset); + } + + int h = 0; + if(caption->GetHeight() > arrow->GetHeight()) + h = caption->GetHeight(); + else + h = arrow->GetHeight(); + + if(this->GetHeight() != h) + this->SetHeight(h); + + if(mode_input) + { + arrow->SetPosition(Vec2i(0,(caption->GetHeight() - arrow->GetHeight()) / 2 )); + caption->SetPosition(Vec2i(arrow->GetWidth() + caption_offset, 0)); + } + else + { + caption->SetPosition(Vec2i(0,0)); + arrow->SetPosition(Vec2i(caption->GetWidth() + caption_offset, (caption->GetHeight() - arrow->GetHeight()) / 2 )); + } +} +void GUIShaderNodeIO::SetBackColor(const Vec4f& color) +{ +} +void GUIShaderNodeIO::OnArrowPress(GUIWindow* w, int arg) +{ + if(arg == KEY_MOUSE_LEFT) + can_drag = true; +} + void GUIShaderNodeIO::OnArrowClick(GUIWindow* w, int arg) + { + Event_PortClick.Invoke(this, arg); + } \ No newline at end of file diff --git a/LifeMaterialEditor/GUIShaderNodeIO.h b/LifeMaterialEditor/GUIShaderNodeIO.h new file mode 100644 index 0000000..0548ace --- /dev/null +++ b/LifeMaterialEditor/GUIShaderNodeIO.h @@ -0,0 +1,41 @@ +#ifndef GUIShaderNodeIO_h__ +#define GUIShaderNodeIO_h__ + +#include +#include +#include +#include "GUINodePort.h" +#include + +class GUIShaderNode; +class GUIShaderNodeIO : public GUIWindow +{ + friend class GUIShaderNode; +public: + EVENT2(IOEvent, GUIShaderNodeIO*, GUIShaderNodeIO*); + IOEvent Event_Link; + + MouseEvent Event_PortClick; + + GUIShaderNodeIO(GUIShaderNode* _parent); + ~GUIShaderNodeIO(); + int id; + void SetMode(bool input); + void Update(float dt); + void RenderSelf(); + void SetBackColor(const Vec4f& color); + void SetCaption(const String& _caption); + bool IsInput(){return mode_input;} + Vec2i GetAttachPoint(); + GUIShaderNode* Owner; + IShaderGraphNodePort* GraphicsPort; +private: + void AcceptLink(GUINodePort* me, GUINodePort* other); + bool can_drag; + void OnArrowPress(GUIWindow* w, int arg); + void OnArrowClick(GUIWindow* w, int arg); + GUINodePort* arrow; + GUIText* caption; + bool mode_input; +}; +#endif // GUIShaderNodeIO_h__ \ No newline at end of file diff --git a/LifeMaterialEditor/GUIShaderNodeTexture.h b/LifeMaterialEditor/GUIShaderNodeTexture.h new file mode 100644 index 0000000..45e13cf --- /dev/null +++ b/LifeMaterialEditor/GUIShaderNodeTexture.h @@ -0,0 +1,44 @@ +#ifndef GUIShaderNodeTexture_h__ +#define GUIShaderNodeTexture_h__ + +#include "GUIShaderNode.h" +#include +#include "Property.h" + +class GUIShaderNodeTexture : public GUIShaderNode +{ +public: + GUIShaderNodeTexture(IShaderGraphNode* src) + :GUIShaderNode(src) + { + tex = 0; + + if(src->GetPropertyByAttribute("Name", "FilePath", &file_prop)) + { + + file_prop->Event_ValueChanged += NewDelegate(this, &GUIShaderNodeTexture::OnTexturePathChanged); + } + + + } + String GetFilename() + { + return file_prop->ToString(); + } +private: + Core::IProperty* file_prop; + void OnTexturePathChanged() + { + //if(tex.ptr()) + //delete tex.ptr(); + printf("Try get texture\n"); + if(file_prop->ToString() != STR("")){ + tex = ResourceManager::me()->GetTexture(file_prop->ToString()); + printf("End try get texture\n"); + SetDisplayImage(tex); + printf("End set display image\n"); + } + } + texture_ptr tex; +}; +#endif // GUIShaderNodeTexture_h__ \ No newline at end of file diff --git a/LifeMaterialEditor/LifeMaterialEditor.cpp b/LifeMaterialEditor/LifeMaterialEditor.cpp new file mode 100644 index 0000000..2152f32 --- /dev/null +++ b/LifeMaterialEditor/LifeMaterialEditor.cpp @@ -0,0 +1,9 @@ +#include "stdafx.h" +#include "Editor.h" + +int _tmain(int argc, _TCHAR* argv[]) +{ + Editor e; + e.Run(); + return 0; +} \ No newline at end of file diff --git a/LifeMaterialEditor/LifeMaterialEditor.vcxproj b/LifeMaterialEditor/LifeMaterialEditor.vcxproj new file mode 100644 index 0000000..f52d7ab --- /dev/null +++ b/LifeMaterialEditor/LifeMaterialEditor.vcxproj @@ -0,0 +1,191 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {4B98EC02-E239-4EA6-9D7A-FC5743184D67} + Win32Proj + LifeMaterialEditor + <Project Location In Database> + <Source Control Database> + <Local Binding Root of Project> + Mercurial Source Control Package + $(VCTargetsPath11) + + + + Application + true + MultiByte + v120 + + + Application + true + MultiByte + v120 + + + Application + false + true + MultiByte + v120 + + + Application + false + true + MultiByte + v120 + + + + + + + + + + + + + + + + + + + true + $(Configuration)\$(ProjectName) + + + true + $(Configuration)\$(ProjectName) + + + false + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)\LifeEngine\include;$(SolutionDir)\LifeCore\include;$(SolutionDir)\LifeSound\include;%(AdditionalIncludeDirectories) + true + false + + + Console + true + $(DXSDK_DIR)\Lib\x86;$(SolutionDir)\LifeCore\lib;$(SolutionDir)\LifeEngine\lib;$(SolutionDir)\LifeSound\lib;%(AdditionalLibraryDirectories) + LifeEngine_d.lib;LifeSound_d.lib;LifeCore_d.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)\LifeEngine\include;$(SolutionDir)\LifeCore\include;$(SolutionDir)\LifeSound\include;%(AdditionalIncludeDirectories) + true + false + + + Console + true + $(DXSDK_DIR)\Lib\x86;$(SolutionDir)\LifeCore\lib;$(SolutionDir)\LifeEngine\lib;$(SolutionDir)\LifeSound\lib;%(AdditionalLibraryDirectories) + LifeEngine_d.lib;LifeSound_d.lib;LifeCore_d.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)\LifeEngine\include;$(SolutionDir)\LifeCore\include;$(SolutionDir)\LifeSound\include;%(AdditionalIncludeDirectories) + true + + + Console + true + true + true + $(DXSDK_DIR)\Lib\x86;$(SolutionDir)\LifeCore\lib;$(SolutionDir)\LifeEngine\lib;$(SolutionDir)\LifeSound\lib;%(AdditionalLibraryDirectories) + LifeEngine.lib;LifeSound.lib;LifeCore.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)\LifeEngine\include;$(SolutionDir)\LifeCore\include;$(SolutionDir)\LifeSound\include;%(AdditionalIncludeDirectories) + true + + + Console + true + true + true + $(DXSDK_DIR)\Lib\x86;$(SolutionDir)\LifeCore\lib;$(SolutionDir)\LifeEngine\lib;$(SolutionDir)\LifeSound\lib;%(AdditionalLibraryDirectories) + LifeEngine.lib;LifeSound.lib;LifeCore.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LifeMaterialEditor/engine_log.txt b/LifeMaterialEditor/engine_log.txt new file mode 100644 index 0000000..00cef14 --- /dev/null +++ b/LifeMaterialEditor/engine_log.txt @@ -0,0 +1,9479 @@ + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + +Closing log + +Logging started! +Mount point is not used! SHOULD BE FIXED! +ResourceManager::MountContentProvider at +root +// +// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111 +// +OLOLO// +// Buffer Definitions: +// +// cbuffer texInfo +// { +// +// float2 texelSize; // Offset: 0 Size: 8 +// float2 dummy; // Offset: 8 Size: 8 [unused] +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim Slot Elements +// ------------------------------ ---------- ------- ----------- ---- -------- +// pointSampler sampler NA NA 0 1 +// diffuseMap texture float4 2d 0 1 +// texInfo cbuffer NA NA 12 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Position 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------ ------ +// SV_Target 0 xyzw 0 TARGET float xyzw +// +ps_4_0 + dcl_immediateConstantBuffer { { -6.000000, 0.001108, 0, 0}, + { -5.000000, 0.004382, 0, 0}, + { -4.000000, 0.013497, 0, 0}, + { -3.000000, 0.032379, 0, 0}, + { -2.000000, 0.060493, 0, 0}, + { -1.000000, 0.088017, 0, 0}, + { 0, 0.099735, 0, 0}, + { 1.000000, 0.088017, 0, 0}, + { 2.000000, 0.060493, 0, 0}, + { 3.000000, 0.032379, 0, 0}, + { 4.000000, 0.013497, 0, 0}, + { 5.000000, 0.004382, 0, 0}, + { 6.000000, 0.001108, 0, 0} } + dcl_constantbuffer cb12[1], immediateIndexed + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + dcl_temps 4 + 0: mov r0.z, l(0) + 1: mov r1.xyzw, l(0,0,0,0) + 2: mov r0.w, l(0) + 3: loop + 4: ige r2.x, r0.w, l(13) + 5: breakc_nz r2.x + 6: mul r0.xy, cb12[0].yxyy, icb[r0.w + 0].xxxx + 7: add r2.xyzw, r0.zxyz, v1.xyxy + 8: sample r3.xyzw, r2.xyxx, t0.xyzw, s0 + 9: mad r3.xyzw, r3.xyzw, icb[r0.w + 0].yyyy, r1.xyzw + 10: sample r2.xyzw, r2.zwzz, t0.xyzw, s0 + 11: mad r1.xyzw, r2.xyzw, icb[r0.w + 0].yyyy, r3.xyzw + 12: iadd r0.w, r0.w, l(1) + 13: endloop + 14: mov o0.xyzw, r1.xyzw + 15: ret +// Approximately 16 instruction slots used + diff --git a/LifeMaterialEditor/stdafx.cpp b/LifeMaterialEditor/stdafx.cpp new file mode 100644 index 0000000..b394884 --- /dev/null +++ b/LifeMaterialEditor/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// LifeMaterialEditor.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file \ No newline at end of file diff --git a/LifeMaterialEditor/stdafx.h b/LifeMaterialEditor/stdafx.h new file mode 100644 index 0000000..fc834b9 --- /dev/null +++ b/LifeMaterialEditor/stdafx.h @@ -0,0 +1,13 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#include +#include + +// TODO: reference additional headers your program requires here \ No newline at end of file diff --git a/LifeMaterialEditor/targetver.h b/LifeMaterialEditor/targetver.h new file mode 100644 index 0000000..5b1f29c --- /dev/null +++ b/LifeMaterialEditor/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include \ No newline at end of file diff --git a/LifeProject.sln b/LifeProject.sln new file mode 100644 index 0000000..c115984 --- /dev/null +++ b/LifeProject.sln @@ -0,0 +1,166 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LifeGame", "LifeGame\LifeGame.vcxproj", "{8BE9EDA5-A27E-48BF-BBA7-36139D47F5D9}" + ProjectSection(ProjectDependencies) = postProject + {3F1B6A31-4B40-4A4D-A213-35AAE300BA1C} = {3F1B6A31-4B40-4A4D-A213-35AAE300BA1C} + {54BC1734-E9F0-4170-A267-91E84F3EACB5} = {54BC1734-E9F0-4170-A267-91E84F3EACB5} + {AE2D1FB3-7F2E-4C11-9B47-5C44D3D3CAA8} = {AE2D1FB3-7F2E-4C11-9B47-5C44D3D3CAA8} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LifeMaterialEditor", "LifeMaterialEditor\LifeMaterialEditor.vcxproj", "{4B98EC02-E239-4EA6-9D7A-FC5743184D67}" + ProjectSection(ProjectDependencies) = postProject + {54BC1734-E9F0-4170-A267-91E84F3EACB5} = {54BC1734-E9F0-4170-A267-91E84F3EACB5} + {AE2D1FB3-7F2E-4C11-9B47-5C44D3D3CAA8} = {AE2D1FB3-7F2E-4C11-9B47-5C44D3D3CAA8} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LifeSound", "LifeSound\LifeSound.vcxproj", "{AE2D1FB3-7F2E-4C11-9B47-5C44D3D3CAA8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MyGame", "MyGame\MyGame.vcxproj", "{55D99FD8-2A69-48FD-A060-D04D5A4A705D}" + ProjectSection(ProjectDependencies) = postProject + {8BE9EDA5-A27E-48BF-BBA7-36139D47F5D9} = {8BE9EDA5-A27E-48BF-BBA7-36139D47F5D9} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LifeAudioPlayer", "LifeAudioPlayer\LifeAudioPlayer.vcxproj", "{4F307D56-E0CB-442C-8E76-FB3C827D54BF}" + ProjectSection(ProjectDependencies) = postProject + {AE2D1FB3-7F2E-4C11-9B47-5C44D3D3CAA8} = {AE2D1FB3-7F2E-4C11-9B47-5C44D3D3CAA8} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestBed", "TestBed\TestBed.vcxproj", "{B7947426-0E53-43A2-9F19-483BF7D205CE}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LifeExport", "LifeExport\LifeExport.vcxproj", "{83915350-90FC-4826-B91F-2AEA0574BFE8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LifeExportInternal", "LifeExportInternal\LifeExportInternal.vcxproj", "{E9BBB863-0237-4F30-B8AD-EFED25A3A61B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LifeWorldEditor", "LifeWorldEditor\LifeWorldEditor.vcxproj", "{999B1AB0-CF3F-46EC-B0B5-164504EE3D6D}" + ProjectSection(ProjectDependencies) = postProject + {3F1B6A31-4B40-4A4D-A213-35AAE300BA1C} = {3F1B6A31-4B40-4A4D-A213-35AAE300BA1C} + {54BC1734-E9F0-4170-A267-91E84F3EACB5} = {54BC1734-E9F0-4170-A267-91E84F3EACB5} + {8BE9EDA5-A27E-48BF-BBA7-36139D47F5D9} = {8BE9EDA5-A27E-48BF-BBA7-36139D47F5D9} + {AE2D1FB3-7F2E-4C11-9B47-5C44D3D3CAA8} = {AE2D1FB3-7F2E-4C11-9B47-5C44D3D3CAA8} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LifeGUI", "LifeGUI\LifeGUI.vcxproj", "{3F1B6A31-4B40-4A4D-A213-35AAE300BA1C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LifeGraphics", "LifeGraphics\LifeGraphics.vcxproj", "{54BC1734-E9F0-4170-A267-91E84F3EACB5}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LifeCore", "LifeCore\LifeCore.vcxproj", "{E83FE0FE-36DC-4F92-B074-5A7280FE6C17}" +EndProject +Global + GlobalSection(MercurialSourceControlSolutionProperties) = preSolution + SolutionIsControlled = True + SolutionBindings = + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8BE9EDA5-A27E-48BF-BBA7-36139D47F5D9}.Debug|Win32.ActiveCfg = Debug|Win32 + {8BE9EDA5-A27E-48BF-BBA7-36139D47F5D9}.Debug|Win32.Build.0 = Debug|Win32 + {8BE9EDA5-A27E-48BF-BBA7-36139D47F5D9}.Debug|x64.ActiveCfg = Debug|x64 + {8BE9EDA5-A27E-48BF-BBA7-36139D47F5D9}.Debug|x64.Build.0 = Debug|x64 + {8BE9EDA5-A27E-48BF-BBA7-36139D47F5D9}.Release|Win32.ActiveCfg = Release|Win32 + {8BE9EDA5-A27E-48BF-BBA7-36139D47F5D9}.Release|Win32.Build.0 = Release|Win32 + {8BE9EDA5-A27E-48BF-BBA7-36139D47F5D9}.Release|x64.ActiveCfg = Release|x64 + {8BE9EDA5-A27E-48BF-BBA7-36139D47F5D9}.Release|x64.Build.0 = Release|x64 + {4B98EC02-E239-4EA6-9D7A-FC5743184D67}.Debug|Win32.ActiveCfg = Debug|Win32 + {4B98EC02-E239-4EA6-9D7A-FC5743184D67}.Debug|Win32.Build.0 = Debug|Win32 + {4B98EC02-E239-4EA6-9D7A-FC5743184D67}.Debug|x64.ActiveCfg = Debug|x64 + {4B98EC02-E239-4EA6-9D7A-FC5743184D67}.Debug|x64.Build.0 = Debug|x64 + {4B98EC02-E239-4EA6-9D7A-FC5743184D67}.Debug|x64.Deploy.0 = Debug|x64 + {4B98EC02-E239-4EA6-9D7A-FC5743184D67}.Release|Win32.ActiveCfg = Release|Win32 + {4B98EC02-E239-4EA6-9D7A-FC5743184D67}.Release|Win32.Build.0 = Release|Win32 + {4B98EC02-E239-4EA6-9D7A-FC5743184D67}.Release|x64.ActiveCfg = Release|x64 + {4B98EC02-E239-4EA6-9D7A-FC5743184D67}.Release|x64.Build.0 = Release|x64 + {AE2D1FB3-7F2E-4C11-9B47-5C44D3D3CAA8}.Debug|Win32.ActiveCfg = Debug|Win32 + {AE2D1FB3-7F2E-4C11-9B47-5C44D3D3CAA8}.Debug|Win32.Build.0 = Debug|Win32 + {AE2D1FB3-7F2E-4C11-9B47-5C44D3D3CAA8}.Debug|x64.ActiveCfg = Debug|x64 + {AE2D1FB3-7F2E-4C11-9B47-5C44D3D3CAA8}.Debug|x64.Build.0 = Debug|x64 + {AE2D1FB3-7F2E-4C11-9B47-5C44D3D3CAA8}.Release|Win32.ActiveCfg = Release|Win32 + {AE2D1FB3-7F2E-4C11-9B47-5C44D3D3CAA8}.Release|Win32.Build.0 = Release|Win32 + {AE2D1FB3-7F2E-4C11-9B47-5C44D3D3CAA8}.Release|x64.ActiveCfg = Release|x64 + {AE2D1FB3-7F2E-4C11-9B47-5C44D3D3CAA8}.Release|x64.Build.0 = Release|x64 + {55D99FD8-2A69-48FD-A060-D04D5A4A705D}.Debug|Win32.ActiveCfg = Debug|Win32 + {55D99FD8-2A69-48FD-A060-D04D5A4A705D}.Debug|Win32.Build.0 = Debug|Win32 + {55D99FD8-2A69-48FD-A060-D04D5A4A705D}.Debug|x64.ActiveCfg = Debug|x64 + {55D99FD8-2A69-48FD-A060-D04D5A4A705D}.Debug|x64.Build.0 = Debug|x64 + {55D99FD8-2A69-48FD-A060-D04D5A4A705D}.Release|Win32.ActiveCfg = Release|Win32 + {55D99FD8-2A69-48FD-A060-D04D5A4A705D}.Release|Win32.Build.0 = Release|Win32 + {55D99FD8-2A69-48FD-A060-D04D5A4A705D}.Release|x64.ActiveCfg = Release|x64 + {55D99FD8-2A69-48FD-A060-D04D5A4A705D}.Release|x64.Build.0 = Release|x64 + {4F307D56-E0CB-442C-8E76-FB3C827D54BF}.Debug|Win32.ActiveCfg = Debug|Win32 + {4F307D56-E0CB-442C-8E76-FB3C827D54BF}.Debug|Win32.Build.0 = Debug|Win32 + {4F307D56-E0CB-442C-8E76-FB3C827D54BF}.Debug|x64.ActiveCfg = Debug|x64 + {4F307D56-E0CB-442C-8E76-FB3C827D54BF}.Debug|x64.Build.0 = Debug|x64 + {4F307D56-E0CB-442C-8E76-FB3C827D54BF}.Release|Win32.ActiveCfg = Release|Win32 + {4F307D56-E0CB-442C-8E76-FB3C827D54BF}.Release|Win32.Build.0 = Release|Win32 + {4F307D56-E0CB-442C-8E76-FB3C827D54BF}.Release|x64.ActiveCfg = Release|x64 + {4F307D56-E0CB-442C-8E76-FB3C827D54BF}.Release|x64.Build.0 = Release|x64 + {B7947426-0E53-43A2-9F19-483BF7D205CE}.Debug|Win32.ActiveCfg = Debug|Win32 + {B7947426-0E53-43A2-9F19-483BF7D205CE}.Debug|Win32.Build.0 = Debug|Win32 + {B7947426-0E53-43A2-9F19-483BF7D205CE}.Debug|x64.ActiveCfg = Debug|x64 + {B7947426-0E53-43A2-9F19-483BF7D205CE}.Debug|x64.Build.0 = Debug|x64 + {B7947426-0E53-43A2-9F19-483BF7D205CE}.Release|Win32.ActiveCfg = Release|Win32 + {B7947426-0E53-43A2-9F19-483BF7D205CE}.Release|Win32.Build.0 = Release|Win32 + {B7947426-0E53-43A2-9F19-483BF7D205CE}.Release|x64.ActiveCfg = Release|x64 + {B7947426-0E53-43A2-9F19-483BF7D205CE}.Release|x64.Build.0 = Release|x64 + {83915350-90FC-4826-B91F-2AEA0574BFE8}.Debug|Win32.ActiveCfg = Debug|Win32 + {83915350-90FC-4826-B91F-2AEA0574BFE8}.Debug|Win32.Build.0 = Debug|Win32 + {83915350-90FC-4826-B91F-2AEA0574BFE8}.Debug|x64.ActiveCfg = Debug|x64 + {83915350-90FC-4826-B91F-2AEA0574BFE8}.Debug|x64.Build.0 = Debug|x64 + {83915350-90FC-4826-B91F-2AEA0574BFE8}.Release|Win32.ActiveCfg = Release|Win32 + {83915350-90FC-4826-B91F-2AEA0574BFE8}.Release|Win32.Build.0 = Release|Win32 + {83915350-90FC-4826-B91F-2AEA0574BFE8}.Release|x64.ActiveCfg = Release|x64 + {83915350-90FC-4826-B91F-2AEA0574BFE8}.Release|x64.Build.0 = Release|x64 + {E9BBB863-0237-4F30-B8AD-EFED25A3A61B}.Debug|Win32.ActiveCfg = Debug|Win32 + {E9BBB863-0237-4F30-B8AD-EFED25A3A61B}.Debug|Win32.Build.0 = Debug|Win32 + {E9BBB863-0237-4F30-B8AD-EFED25A3A61B}.Debug|x64.ActiveCfg = Debug|x64 + {E9BBB863-0237-4F30-B8AD-EFED25A3A61B}.Debug|x64.Build.0 = Debug|x64 + {E9BBB863-0237-4F30-B8AD-EFED25A3A61B}.Release|Win32.ActiveCfg = Release|Win32 + {E9BBB863-0237-4F30-B8AD-EFED25A3A61B}.Release|Win32.Build.0 = Release|Win32 + {E9BBB863-0237-4F30-B8AD-EFED25A3A61B}.Release|x64.ActiveCfg = Release|x64 + {E9BBB863-0237-4F30-B8AD-EFED25A3A61B}.Release|x64.Build.0 = Release|x64 + {999B1AB0-CF3F-46EC-B0B5-164504EE3D6D}.Debug|Win32.ActiveCfg = Debug|Win32 + {999B1AB0-CF3F-46EC-B0B5-164504EE3D6D}.Debug|Win32.Build.0 = Debug|Win32 + {999B1AB0-CF3F-46EC-B0B5-164504EE3D6D}.Debug|x64.ActiveCfg = Debug|x64 + {999B1AB0-CF3F-46EC-B0B5-164504EE3D6D}.Debug|x64.Build.0 = Debug|x64 + {999B1AB0-CF3F-46EC-B0B5-164504EE3D6D}.Release|Win32.ActiveCfg = Release|Win32 + {999B1AB0-CF3F-46EC-B0B5-164504EE3D6D}.Release|Win32.Build.0 = Release|Win32 + {999B1AB0-CF3F-46EC-B0B5-164504EE3D6D}.Release|x64.ActiveCfg = Release|x64 + {999B1AB0-CF3F-46EC-B0B5-164504EE3D6D}.Release|x64.Build.0 = Release|x64 + {3F1B6A31-4B40-4A4D-A213-35AAE300BA1C}.Debug|Win32.ActiveCfg = Debug|Win32 + {3F1B6A31-4B40-4A4D-A213-35AAE300BA1C}.Debug|Win32.Build.0 = Debug|Win32 + {3F1B6A31-4B40-4A4D-A213-35AAE300BA1C}.Debug|x64.ActiveCfg = Debug|x64 + {3F1B6A31-4B40-4A4D-A213-35AAE300BA1C}.Debug|x64.Build.0 = Debug|x64 + {3F1B6A31-4B40-4A4D-A213-35AAE300BA1C}.Release|Win32.ActiveCfg = Release|Win32 + {3F1B6A31-4B40-4A4D-A213-35AAE300BA1C}.Release|Win32.Build.0 = Release|Win32 + {3F1B6A31-4B40-4A4D-A213-35AAE300BA1C}.Release|x64.ActiveCfg = Release|x64 + {3F1B6A31-4B40-4A4D-A213-35AAE300BA1C}.Release|x64.Build.0 = Release|x64 + {54BC1734-E9F0-4170-A267-91E84F3EACB5}.Debug|Win32.ActiveCfg = Debug|Win32 + {54BC1734-E9F0-4170-A267-91E84F3EACB5}.Debug|Win32.Build.0 = Debug|Win32 + {54BC1734-E9F0-4170-A267-91E84F3EACB5}.Debug|x64.ActiveCfg = Debug|x64 + {54BC1734-E9F0-4170-A267-91E84F3EACB5}.Debug|x64.Build.0 = Debug|x64 + {54BC1734-E9F0-4170-A267-91E84F3EACB5}.Release|Win32.ActiveCfg = Release|Win32 + {54BC1734-E9F0-4170-A267-91E84F3EACB5}.Release|Win32.Build.0 = Release|Win32 + {54BC1734-E9F0-4170-A267-91E84F3EACB5}.Release|x64.ActiveCfg = Release|x64 + {54BC1734-E9F0-4170-A267-91E84F3EACB5}.Release|x64.Build.0 = Release|x64 + {E83FE0FE-36DC-4F92-B074-5A7280FE6C17}.Debug|Win32.ActiveCfg = Debug|Win32 + {E83FE0FE-36DC-4F92-B074-5A7280FE6C17}.Debug|Win32.Build.0 = Debug|Win32 + {E83FE0FE-36DC-4F92-B074-5A7280FE6C17}.Debug|x64.ActiveCfg = Debug|x64 + {E83FE0FE-36DC-4F92-B074-5A7280FE6C17}.Debug|x64.Build.0 = Debug|x64 + {E83FE0FE-36DC-4F92-B074-5A7280FE6C17}.Release|Win32.ActiveCfg = Release|Win32 + {E83FE0FE-36DC-4F92-B074-5A7280FE6C17}.Release|Win32.Build.0 = Release|Win32 + {E83FE0FE-36DC-4F92-B074-5A7280FE6C17}.Release|x64.ActiveCfg = Release|x64 + {E83FE0FE-36DC-4F92-B074-5A7280FE6C17}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(SubversionScc) = preSolution + Manager = AnkhSVN - Subversion Support for Visual Studio + Svn-Managed = True + EndGlobalSection +EndGlobal diff --git a/LifeSound/LifeSound.vcxproj b/LifeSound/LifeSound.vcxproj new file mode 100644 index 0000000..f520aca --- /dev/null +++ b/LifeSound/LifeSound.vcxproj @@ -0,0 +1,189 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + {AE2D1FB3-7F2E-4C11-9B47-5C44D3D3CAA8} + Win32Proj + LifeSound + <Project Location In Database> + <Source Control Database> + <Local Binding Root of Project> + Mercurial Source Control Package + $(VCTargetsPath11) + + + + StaticLibrary + true + MultiByte + v120 + + + StaticLibrary + true + MultiByte + v120 + + + StaticLibrary + false + true + MultiByte + v120 + + + StaticLibrary + false + true + MultiByte + v120 + + + + + + + + + + + + + + + + + + + $(SolutionDir)$(ProjectName)\lib\ + $(SolutionDir)$(ProjectName)\tmp\$(Configuration)\ + + + $(ProjectName)_d + $(SolutionDir)$(ProjectName)\tmp\$(Configuration)\ + $(SolutionDir)$(ProjectName)\lib\ + + + + + + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + C:\Program Files %28x86%29\Visual Leak Detector\include;.\src;$(REPO_DIR)\OpenAL\include;$(SolutionDir)\LifeCore\src;$(REPO_DIR)\libvorbis\include;$(REPO_DIR)\libogg\include;%(AdditionalIncludeDirectories) + true + false + + + Windows + true + + + $(OutDir)$(TargetName)$(TargetExt) + C:\Program Files %28x86%29\Visual Leak Detector\lib;$(REPO_DIR)\OpenAL\libs\Win32;$(SolutionDir)\LifeCore\lib;$(REPO_DIR)\libvorbis\win32\VS2010\Win32\Release;$(REPO_DIR)\libogg\win32\VS2010\Win32\Release + LifeCore_d.lib;OpenAL32.lib + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + C:\Program Files %28x86%29\Visual Leak Detector\include;.\include;$(REPO_DIR)\OpenAL\include;$(SolutionDir)\LifeCore\include;$(REPO_DIR)\libvorbis\include;$(REPO_DIR)\libogg\include;%(AdditionalIncludeDirectories) + true + + + Windows + true + + + lib\LifeSound_d.lib + C:\Program Files %28x86%29\Visual Leak Detector\lib;$(REPO_DIR)\OpenAL\libs\Win32;$(SolutionDir)\LifeCore\lib;$(REPO_DIR)\libvorbis\win32\VS2010\Win32\Release;$(REPO_DIR)\libogg\win32\VS2010\Win32\Release + LifeCore_d.lib;libvorbis.lib;libogg.lib;libvorbisfile.lib;OpenAL32.lib + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + .\src;$(REPO_DIR)\OpenAL\include;$(SolutionDir)\LifeCore\src;$(REPO_DIR)\libvorbis\include;$(REPO_DIR)\libogg\include;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + + + $(OutDir)$(TargetName)$(TargetExt) + LifeCore.lib;OpenAL32.lib + $(REPO_DIR)\OpenAL\libs\Win32;$(SolutionDir)\LifeCore\lib;$(REPO_DIR)\libvorbis\win32\VS2010\Win32\Release;$(REPO_DIR)\libogg\win32\VS2010\Win32\Release + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + .\include;$(REPO_DIR)\OpenAL\include;$(SolutionDir)\LifeCore\include;$(REPO_DIR)\libvorbis\include;$(REPO_DIR)\libogg\include;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + + + lib\LifeSound.lib + LifeCore.lib;libvorbis.lib;libogg.lib;libvorbisfile.lib;OpenAL32.lib + $(REPO_DIR)\OpenAL\libs\Win32;$(SolutionDir)\LifeCore\lib;$(REPO_DIR)\libvorbis\win32\VS2010\Win32\Release;$(REPO_DIR)\libogg\win32\VS2010\Win32\Release + + + + + + \ No newline at end of file diff --git a/LifeSound/src/ISound.h b/LifeSound/src/ISound.h new file mode 100644 index 0000000..5d95302 --- /dev/null +++ b/LifeSound/src/ISound.h @@ -0,0 +1,33 @@ +#ifndef ISound_h__ +#define ISound_h__ + +#include +#include + +namespace Sound +{ + enum STREAMING_MODE + { + SM_DISABLED = 0, + SM_ENABLED = 1, + SM_AUTO = 2 + }; + class ISound + { + public: + ISound() {} + virtual void Play() = 0; + virtual void Stop() = 0; + virtual void Update() = 0; + virtual void Move(const LifeMath::Vec3f& position) = 0; + virtual bool IsPlaying() = 0; + virtual size_t GetPlaybackPosition() = 0; + virtual size_t GetByteLength(){return 0;} + virtual void SetPlaybackPosition(float pos){} + virtual double GetTimeLength(){return 0;} + virtual double GetTimePosition(){return 0;} + virtual void SeekTime(double position){} + virtual ~ISound() {} + }; +} +#endif // Sound_h__ \ No newline at end of file diff --git a/LifeSound/src/ISoundStream.cpp b/LifeSound/src/ISoundStream.cpp new file mode 100644 index 0000000..fc00cc3 --- /dev/null +++ b/LifeSound/src/ISoundStream.cpp @@ -0,0 +1,5 @@ +#include "ISoundStream.h" + +namespace Sound +{ +} \ No newline at end of file diff --git a/LifeSound/src/ISoundStream.h b/LifeSound/src/ISoundStream.h new file mode 100644 index 0000000..3415c67 --- /dev/null +++ b/LifeSound/src/ISoundStream.h @@ -0,0 +1,14 @@ +#ifndef ISoundStream_h__ +#define ISoundStream_h__ + +namespace Sound +{ + class ISoundStream + { + public: + ISoundStream(); + virtual ~ISoundStream(); + int seek(); + }; +} +#endif // ISoundStream_h__ \ No newline at end of file diff --git a/LifeSound/src/OggStream.cpp b/LifeSound/src/OggStream.cpp new file mode 100644 index 0000000..e83ed85 --- /dev/null +++ b/LifeSound/src/OggStream.cpp @@ -0,0 +1,158 @@ +#include "OggStream.h" + +namespace Sound +{ + size_t ReadOgg(void *ptr, size_t size, size_t nmemb, void *datasource) + { + LifeCore::IDataStream *File = reinterpret_cast(datasource); + int countRead = File->Read(ptr, size * nmemb); + return countRead; + } + int SeekOgg(void *datasource, ogg_int64_t offset, int whence) + { + LifeCore::IDataStream *File = reinterpret_cast(datasource); + int pos = 0; + switch (whence) + { + case SEEK_SET: pos = (int)offset; break; + case SEEK_CUR: pos = (int)offset + File->Tell(); break; + case SEEK_END: pos = (int)offset + File->Length(); break; + default: return -1; + } + if(pos > File->Length()) pos = File->Length(); + File->Seek(pos); + return 0; + } + long TellOgg(void *datasource) + { + LifeCore::IDataStream *File = reinterpret_cast(datasource); + return File->Tell(); + } + int CloseOgg(void *datasource) + { + if(datasource) + { + LifeCore::IDataStream *File = reinterpret_cast(datasource); + File->Close(); + } + return 0; + } + ALint OggStream::GetPosition() + { + return position; + } + OggStream::OggStream(LifeCore::IFileSystem* fs) + :ogg(0), position(0) + { + fileSystem = fs; + } + OggStream::~OggStream() + { + if(!ogg) + { + ov_clear(ogg); + delete ogg; + } + } + int OggStream::Open(const String& fn) + { + name = fn; + + LifeCore::IFile* f; + + if(fileSystem->GetFile(name, &f) != SC_OK) + return SC_FAIL; + + stm = f->OpenRead(); + + ogg = new OggVorbis_File(); + ov_callbacks cb; + cb.read_func = ReadOgg; + cb.close_func = CloseOgg; + cb.seek_func = SeekOgg; + cb.tell_func = TellOgg; + + ov_open_callbacks(stm.get(), ogg, NULL, -1, cb); + + //get uncompressed PCM data size + + vorbis_info * oggInfo; + oggInfo = ov_info(ogg, -1); + rate = oggInfo->rate; + format = (oggInfo->channels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16; + + pcmsize = ov_pcm_total(ogg, -1) * 2 * oggInfo->channels; + return SC_OK; + } + + ALuint OggStream::GetFormat() + { + return format; + } + ALuint OggStream::GetRate() + { + return rate; + } + ALuint OggStream::GetPCMSize() + { + return pcmsize; + } + size_t OggStream::GetSourceSize() + { + return stm->Length(); + } + void OggStream::SeekPCM(int _position) + { + int r = ov_pcm_seek(ogg, _position); + position = _position; + } + double OggStream::GetTimeLength() + { + return ov_time_total(ogg, -1); + } + void OggStream::SeekTime(double time) + { + ov_time_seek(ogg, time); + } + double OggStream::GetTimePosition() + { + return ov_time_tell(ogg); + } + ALuint OggStream::Read(char* pData, int length, bool readLooped) + { + if(!length) return 0; + int section; + char* data = pData; + int totalRead = 0; + + while(totalRead < length) + { + int readCount = ov_read(ogg, data + totalRead, length - totalRead, 0, 2, 1, §ion); + + if(readCount < 0) //error + { + return -1; + } + else if (readCount > 0) //read successful + { + totalRead += readCount; + position+=readCount; + } + else //end of PCM stream + { + if(readLooped) + { + ov_pcm_seek(ogg, 0); + position = 0; + } + else + { + //memset(data + totalRead, 0, length - totalRead); + return totalRead; + } + } + } + + return totalRead; + } +} \ No newline at end of file diff --git a/LifeSound/src/OggStream.h b/LifeSound/src/OggStream.h new file mode 100644 index 0000000..418b6dd --- /dev/null +++ b/LifeSound/src/OggStream.h @@ -0,0 +1,45 @@ +#ifndef OggStream_h__ +#define OggStream_h__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Sound +{ + class OggStream + { + public: + OggStream(LifeCore::IFileSystem* fs); + ~OggStream(); + int Open(const String& fn); + ALuint GetFormat(); + ALuint GetRate(); + ALuint GetPCMSize(); + void SeekPCM(int position); + ALuint Read(char* pData, int length, bool readLooped); + ALint GetPosition(); + size_t GetSourceSize(); + double GetTimeLength(); + double GetTimePosition(); + void SeekTime(double time); + //size_t GetSize(); + private: + size_t position; + LifeCore::IFileSystem* fileSystem; + LifeCore::IDataStreamPtr stm; + String name; + vorbis_info info; + ALuint rate; + ALuint format; + ALuint pcmsize; + OggVorbis_File* ogg; + }; +} +#endif // OggStream_h__ \ No newline at end of file diff --git a/LifeSound/src/SoundInfo.h b/LifeSound/src/SoundInfo.h new file mode 100644 index 0000000..1415384 --- /dev/null +++ b/LifeSound/src/SoundInfo.h @@ -0,0 +1,16 @@ +#ifndef SoundBuffer_h__ +#define SoundBuffer_h__ + +//#define BUFFER_SIZE (4096 * 8) + +namespace Sound +{ + struct SoundInfo + { + unsigned int id; + unsigned int format; + unsigned int rate; + unsigned int size; + }; +} +#endif // SoundBuffer_h__ \ No newline at end of file diff --git a/LifeSound/src/SoundSystem.cpp b/LifeSound/src/SoundSystem.cpp new file mode 100644 index 0000000..fd9e9f9 --- /dev/null +++ b/LifeSound/src/SoundSystem.cpp @@ -0,0 +1,154 @@ +#include "SoundSystem.h" +#include +#include +#include +#include +#include +#include +#include "OggStream.h" +#include "StaticSound.h" +#include "StreamedSound.h" + +//#define BUFFER_SIZE (4096 * 8) + +#include + +namespace Sound +{ + static ALCdevice* dev; + static ALCcontext* context; + static ALfloat ListenerPos[] = { 0.0f, 0.0f, 0.0f }; + + static std::vector sounds; + + // Ñêîðîñòü ñëóøàòåëÿ. + static ALfloat ListenerVel[] = { 0.0f, 0.0f, 0.0f }; + + // Îðèåíòàöèÿ ñëóøàòåëÿ. (Ïåðâûå 3 ýëåìåíòà – íàïðàâëåíèå «íà», ïîñëåäíèå 3 – «ââåðõ») + static ALfloat ListenerOri[] = { 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f }; + + SoundSystem::SoundSystem() + { + } + SoundSystem::~SoundSystem() + { + for(CacheMapIter it = staticCache.begin(); it != staticCache.end(); it++) + { + if(it->second) + { + if(alIsBuffer(it->second->id)) + { + alDeleteBuffers(1, &it->second->id); + } + } + } + + alcDestroyContext(context); + alcCloseDevice(dev); + } + int SoundSystem::Init(LifeCore::IFileSystem* fs) + { + fileSystem = fs; + + char* DefaultDevice = (char*)alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); + printf("%s\n", DefaultDevice); + + dev = alcOpenDevice("Generic Software"); + + ALenum error = alGetError(); + if(error!= AL_NO_ERROR) + { + printf("Error initializing sound device context!\n"); + return -1; + } + context = alcCreateContext(dev, NULL); + if (alGetError() != AL_NO_ERROR) + return AL_FALSE; + + alcMakeContextCurrent(context); + if (alGetError() != AL_NO_ERROR) + return AL_FALSE; + alListenerfv(AL_POSITION, ListenerPos); + alListenerfv(AL_VELOCITY, ListenerVel); + alListenerfv(AL_ORIENTATION, ListenerOri); + + return 0; + } + + void SoundSystem::Update() + { + for(std::vector::iterator it = sounds.begin(); it != sounds.end(); it++) + { + (*it)->Update(); + } + } + + ISound* SoundSystem::getSound(const String& name, int streamingMode) + { + switch(streamingMode) + { + case SM_ENABLED: + { + OggStream* s = new OggStream(fileSystem); + s->Open(name); + ISound* sound = new StreamedSound(s, true); + sounds.push_back(sound); + return sound; + } + break; + case SM_DISABLED: + { + CacheMapIter it = staticCache.find(name); + SoundInfo* src = 0; + if(it != staticCache.end()) + { + src = it->second; + } + else + { + OggStream s(fileSystem); + s.Open(name); + + src = new SoundInfo(); + src->size = s.GetPCMSize(); + src->format = s.GetFormat(); + src->rate = s.GetRate(); + + alGenBuffers(1, &src->id); + + //read all PCM data + char* pcmData = new char[src->size]; + if(s.Read(pcmData, src->size, false) != src->size) + { + //error!!!!! + } + + //copy PCM data to OpenAL sound buffer + alBufferData(src->id, src->format, pcmData, src->size, src->rate); + + //delete temporary PCM buffer + delete[] pcmData; + + staticCache[name] = src; + } + + if(src) + { + ISound* s = new StaticSound(src, true); + sounds.push_back(s); + return s; + } + else //error + { + } + } + break; + case SM_AUTO: + { + } + break; + } + + return 0; + } +} \ No newline at end of file diff --git a/LifeSound/src/SoundSystem.h b/LifeSound/src/SoundSystem.h new file mode 100644 index 0000000..a9bd62c --- /dev/null +++ b/LifeSound/src/SoundSystem.h @@ -0,0 +1,27 @@ +#ifndef SoundSystem_h__ +#define SoundSystem_h__ + +#include +#include "ISound.h" +#include +#include "SoundInfo.h" +#include + +namespace Sound +{ + class SoundSystem + { + private: + typedef std::map CacheMap; + typedef std::map::iterator CacheMapIter; + LifeCore::IFileSystem* fileSystem; + CacheMap staticCache; + public: + SoundSystem(); + ~SoundSystem(); + int Init(LifeCore::IFileSystem* fs); + void Update(); + ISound* getSound(const String& name, int streamingMode); + }; +} +#endif // SoundSystem_h__ \ No newline at end of file diff --git a/LifeSound/src/StaticSound.cpp b/LifeSound/src/StaticSound.cpp new file mode 100644 index 0000000..a3ee5c3 --- /dev/null +++ b/LifeSound/src/StaticSound.cpp @@ -0,0 +1,55 @@ +#include "StaticSound.h" +#include + +namespace Sound +{ + StaticSound::StaticSound(SoundInfo* src, bool looped) + { + velocity = position = LifeMath::Vec3f(0.0f, 0.0f, 0.0f); + + data = src; + alGenSources(1, &source); + if (alGetError() != AL_NO_ERROR) + printf("FUCK!"); + alSourcef (source, AL_PITCH, 1.0f); + alSourcef (source, AL_GAIN, 1.0f); + alSourcefv(source, AL_POSITION, reinterpret_cast(&position.X)); + alSourcefv(source, AL_VELOCITY, reinterpret_cast(&velocity.X)); + alSourcei (source, AL_LOOPING, looped); + + //attach sound buffer to source + alSourcei (source, AL_BUFFER, src->id); + if (alGetError() != AL_NO_ERROR) + printf("FUCK!"); + } + size_t StaticSound::GetPlaybackPosition() + { + return 0; + } + StaticSound::~StaticSound() + { + if(alIsSource(source)) + alDeleteSources(1, &source); + } + void StaticSound::Play() + { + alSourcePlay(source); + } + bool StaticSound::IsPlaying() + { + return true; + } + void StaticSound::Stop() + { + alSourceStop(source); + } + void StaticSound::Update() + { + return; + } + void StaticSound::Move(const LifeMath::Vec3f& pos) + { + position = pos; + alSourcefv(source, AL_POSITION, reinterpret_cast(&position.X)); + } +} \ No newline at end of file diff --git a/LifeSound/src/StaticSound.h b/LifeSound/src/StaticSound.h new file mode 100644 index 0000000..728b456 --- /dev/null +++ b/LifeSound/src/StaticSound.h @@ -0,0 +1,30 @@ +#ifndef StaticSound_h__ +#define StaticSound_h__ + +#include "ISound.h" +#include +#include +#include "SoundInfo.h" + +namespace Sound +{ + class StaticSound : public ISound + { + public: + StaticSound(SoundInfo* src, bool looped); + ~StaticSound(); + void Play(); + void Stop(); + void Update(); + void Move(const LifeMath::Vec3f& pos); + bool IsPlaying(); + size_t GetPlaybackPosition(); + private: + ALuint source; + LifeMath::Vec3f position; + LifeMath::Vec3f velocity; + SoundInfo* data; + }; +} + +#endif // StaticSound_h__ \ No newline at end of file diff --git a/LifeSound/src/StreamedSound.cpp b/LifeSound/src/StreamedSound.cpp new file mode 100644 index 0000000..63f609a --- /dev/null +++ b/LifeSound/src/StreamedSound.cpp @@ -0,0 +1,137 @@ +#include "StreamedSound.h" + +namespace Sound +{ + StreamedSound::StreamedSound(OggStream* stm, bool looped) + :loop(looped), stopped(true) + { + data = stm; + + velocity = position = LifeMath::Vec3f(0.0f, 0.0f, 0.0f); + ALenum error; + + data = stm; + alGenSources(1, &source); + if (alGetError() != AL_NO_ERROR) + printf("FUCK!"); + alSourcef (source, AL_PITCH, 1.0f); + alSourcef (source, AL_GAIN, 1.0f); + alSourcefv(source, AL_POSITION, reinterpret_cast(&position.X)); + alSourcefv(source, AL_VELOCITY, reinterpret_cast(&velocity.X)); + alSourcei (source, AL_LOOPING, AL_FALSE); + + error = alGetError(); + + if (error != AL_NO_ERROR) + printf("%s\n", (char *)alGetString(error)); + //attach sound buffer to source + //alSourcei (source, AL_BUFFER, src->id); + + for(int i = 0; i< BUFFERS_COUNT; i++) + { + alGenBuffers(BUFFERS_COUNT, &buffers[i]); + + error = alGetError(); + + if (error != AL_NO_ERROR) + printf("%s\n", (char *)alGetString(error)); + + if(!alIsBuffer(buffers[i])) + { + printf("TRINDEC!!!!!\n"); + } + + stm->Read(tempBuffer, BUFFER_SIZE, looped); + alBufferData(buffers[i], stm->GetFormat(), &tempBuffer[0], BUFFER_SIZE, stm->GetRate() ); + + error = alGetError(); + + if (error != AL_NO_ERROR) + printf("%s\n", (char *)alGetString(error)); + + alSourceQueueBuffers(source, 1, &buffers[i]); + } + + error = alGetError(); + + if (error != AL_NO_ERROR) + printf("%s\n", (char *)alGetString(error)); + } + StreamedSound::~StreamedSound() + { + } + size_t StreamedSound::GetByteLength() + { + return data->GetPCMSize(); + //return data->GetSourceSize(); + } + double StreamedSound::GetTimeLength() + { + return data->GetTimeLength(); + } + double StreamedSound::GetTimePosition() + { + return data->GetTimePosition(); + } + void StreamedSound::SetPlaybackPosition(float pos) + { + int a = GetPlaybackPosition(); + data->SeekPCM(int(float(data->GetPCMSize()) * pos)); + } + void StreamedSound::SeekTime(double position) + { + data->SeekTime(position); + } + size_t StreamedSound::GetPlaybackPosition() + { + //ALint val; + //alGetSourcei(source, AL_BYTE_OFFSET, &val); + return abs(data->GetPosition()); + } + bool StreamedSound::IsPlaying() + { + return !stopped; + } + void StreamedSound::Play() + { + alSourcePlay(source); + stopped = false; + } + void StreamedSound::Stop() + { + alSourceStop(source); + stopped = true; + } + void StreamedSound::Update() + { + if(!stopped) + { + int procCount = 0; + alGetSourcei(source, AL_BUFFERS_PROCESSED, &procCount); + ALuint bid; + + while(procCount--) + { + alSourceUnqueueBuffers(source, 1, &bid); + int cr = data->Read(tempBuffer, BUFFER_SIZE, loop); + + if(cr == 0) + { + if(!loop) + { + Stop(); + data->SeekPCM(0); + } + } + else + { + alBufferData(bid, data->GetFormat(), tempBuffer, cr, data->GetRate() ); + alSourceQueueBuffers(source, 1, &bid); + } + } + } + } + void StreamedSound::Move(const LifeMath::Vec3f& pos) + { + } +} \ No newline at end of file diff --git a/LifeSound/src/StreamedSound.h b/LifeSound/src/StreamedSound.h new file mode 100644 index 0000000..3a79f7e --- /dev/null +++ b/LifeSound/src/StreamedSound.h @@ -0,0 +1,41 @@ +#ifndef StreamedSound_h__ +#define StreamedSound_h__ + +#include "OggStream.h" +#include +#include +#include "ISound.h" + +namespace Sound +{ +#define BUFFERS_COUNT 2 +#define BUFFER_SIZE (4096 * 16) + + class StreamedSound : public ISound + { + public: + StreamedSound(OggStream* stm, bool looped); + ~StreamedSound(); + void Play(); + void Stop(); + void Update(); + void Move(const LifeMath::Vec3f& pos); + bool IsPlaying(); + size_t GetPlaybackPosition(); + size_t GetByteLength(); + void SetPlaybackPosition(float pos); + double GetTimeLength(); + void SeekTime(double position); + double GetTimePosition(); + private: + ALuint source; + LifeMath::Vec3f position; + LifeMath::Vec3f velocity; + OggStream* data; + char tempBuffer[BUFFER_SIZE]; + ALuint buffers[BUFFERS_COUNT]; + bool loop; + bool stopped; + }; +} +#endif // StreamedSound_h__ \ No newline at end of file diff --git a/LifeWorldEditor/EditorCameraInputController.h b/LifeWorldEditor/EditorCameraInputController.h new file mode 100644 index 0000000..1444bbf --- /dev/null +++ b/LifeWorldEditor/EditorCameraInputController.h @@ -0,0 +1,13 @@ +#ifndef EditorCameraInputController_h__ +#define EditorCameraInputController_h__ + +#include + +namespace LifeWorldEditor +{ + class GOCEditorCameraInputController : public LifeGame::GOCLogic + { + + }; +} +#endif // EditorCameraInputController_h__ diff --git a/LifeWorldEditor/LifeWorldEditor.cpp b/LifeWorldEditor/LifeWorldEditor.cpp new file mode 100644 index 0000000..f35e5d5 --- /dev/null +++ b/LifeWorldEditor/LifeWorldEditor.cpp @@ -0,0 +1,151 @@ +#include "LifeWorldEditor.h" +#include +#include +#include +#include +#include +#include +#include +#include + + + +LifeWorldEditor::LifeWorldEditor(void){ + +} + +LifeWorldEditor::~LifeWorldEditor(void){ + +} + +int32 LifeWorldEditor::Load(){ + int32 window_width = 1024; + int32 window_height = 600; + + main_window = new RenderWindowWin(); + main_window->SetSize(window_width, window_height); + main_window->CenterWindow(); + + game = new LifeGame::Game(); + game->SetResourcesPath(STR("D:\\!development\\Programming\\Projects\\Data\\")); + game->InitInput(window_width, window_height, main_window); + game->InitVideo(LifeGraphics::RT_DIRECTX_11, window_width, window_height); + + + main_out = game->GetGraphics()->CreateRenderOutput(main_window); + + editor_scene = new LifeGame::World(game, new LifeGame::IPhysicsWorld(), main_out); + game->GetInput()->AddListener(editor_scene); + + + LifeGUI::GUIOverlay* ovrl = editor_scene->GetGUI()->GetOverlay("MainGui", true); + editor_scene->GetGUI()->SwitchOverlay("MainGui"); + + LifeGUI::GUIToolStrip* main_menu = new LifeGUI::GUIToolStrip(); + main_menu->SetDock(LifeGUI::DOCK_TOP); + main_menu->SetHeight(20); + ovrl->AddChild(main_menu); + + LifeGUI::GUIToolStripMenu* menu1 = new LifeGUI::GUIToolStripMenu(); + menu1->SetCaption("Trololo"); + menu1->GetMenu()->AddItem("OMFG",false); + main_menu->AddItem(menu1); + + + LifeGUI::GUISplitPanel* split_container = new LifeGUI::GUISplitPanel(); + split_container->SetDock(LifeGUI::DOCK_FILL); + split_container->SetSplitterHorisontal(false); + ovrl->AddChild(split_container); + + + LifeGUI::GUIRenderWindow* wnd = new LifeGUI::GUIRenderWindow(); + wnd->GetGUIWindow()->SetDock(LifeGUI::DOCK_FILL); + split_container->GetContainer1()->AddChild(wnd->GetGUIWindow()); + LifeGame::GUIOutput* scene_out = LifeGame::GUIOutput::Create(wnd, game->GetGraphics()->GetRenderer()); + + wnd->GetGUIWindow()->SetPicture( LifeGUI::GUIManager::me()->GetDataManager()->getImage("alpha.bmp")); + + + game_scene = new LifeGame::World(game, new LifeGame::IPhysicsWorld(), scene_out); + wnd->GetInputSystem()->AddListener(game_scene); + LifeGUI::GUIOverlay* hud = game_scene->GetGUI()->GetOverlay("HUD", true); + game_scene->GetGUI()->SwitchOverlay("HUD"); + LifeGUI::GUIToolStrip* main_game_menu = new LifeGUI::GUIToolStrip(); + main_game_menu->SetDock(LifeGUI::DOCK_TOP); + main_game_menu->SetHeight(20); + hud->AddChild(main_game_menu); + + LifeGUI::GUIToolStripMenu* menu = new LifeGUI::GUIToolStripMenu(); + menu->SetCaption("Trololo"); + menu->GetMenu()->AddItem("OMFG",false); + main_game_menu->AddItem(menu); + + /* LifeGame::Player* player = (LifeGame::Player*)LifeGame::EntityFactory::me()->BuildEntity(AS_STRING("root/Data/Entities/thing.xml"), game); + + player->GetTransform()->SetPosition(LifeMath::float3(0.3f,0.5f,0.2f)); + player->GetTransform()->SetRotation(LifeMath::float3(0.0f,1.0f,0.0f), 1.0f); + + game_scene->AddEntity(player);*/ + + + LifeGraphics::Light* l = new LifeGraphics::Light(LifeGraphics::LT_POINT); + l->setRange(20); + game_scene->GetGraphicsScene()->PushNode(l); + l->getTransform()->SetPosition(LifeMath::float3(4,0.5f,-4.0f)); + l->color = LifeMath::float3(1,1,1); + l->intensity = 1.0f; + + LifeGraphics::IObjectMesh* tower = new LifeGraphics::IObjectMesh("root/Data/Objects/Tower_opt.vmf"); + game_scene->GetGraphicsScene()->PushNode(tower); + tower->getTransform()->SetPosition(LifeMath::float3(0,0,0)); + + LifeGame::GameCamera* mainCamera = new LifeGame::GameCamera(1.33f); + mainCamera->GetTransform()->SetPosition(LifeMath::float3(0.3f,0.5f,-0.5f)); + mainCamera->getCam()->setDirection(LifeMath::float3(0,0.5f, 0)); + //mainCamera->GetTransform()->SetRotation(LifeMath::float3(0.0f,1.0f,0.0f), 1.0f); + game_scene->AddEntity(mainCamera); + game_scene->SetMainCamera(mainCamera); + + + LifeGame::ActionSystem::me()->AddAction("MoveForward"); + LifeGame::ActionSystem::me()->AddAction("MoveBackward"); + LifeGame::ActionSystem::me()->AddAction("MoveLeft"); + LifeGame::ActionSystem::me()->AddAction("MoveRight"); + LifeGame::ActionSystem::me()->AddAction("RotateYaw"); + LifeGame::ActionSystem::me()->AddAction("RotatePitch"); + + LifeGame::InputActionProvider* input_action = new LifeGame::InputActionProvider(); + input_action->Map(LifeCore::KEY_W, LifeCore::KA_HOLD, "MoveForward"); + input_action->Map(LifeCore::KEY_A, LifeCore::KA_HOLD, "MoveLeft"); + input_action->Map(LifeCore::KEY_D, LifeCore::KA_HOLD, "MoveRight"); + input_action->Map(LifeCore::KEY_S, LifeCore::KA_HOLD, "MoveBackward"); + input_action->Map(LifeCore::MOUSE_AXIS_X, "RotateYaw"); + input_action->Map(LifeCore::MOUSE_AXIS_Y, "RotatePitch"); + + game_scene->AddActionProvider(input_action); + input_action->SetInputSystem(wnd->GetInputSystem()); + + LifeGame::Entity* editor_player = new LifeGame::Entity(); + LifeGame::GOCCameraFree* editor_player_controller = new LifeGame::GOCCameraFree(); + editor_player_controller->SetCamera(mainCamera); + editor_player->AddLogicEntity(editor_player_controller); + game_scene->AddEntity(editor_player); + + LifeGame::ActionSystem::me()->GetAction("MoveForward")->AddListener(editor_player_controller); + LifeGame::ActionSystem::me()->GetAction("MoveLeft")->AddListener(editor_player_controller); + LifeGame::ActionSystem::me()->GetAction("MoveBackward")->AddListener(editor_player_controller); + LifeGame::ActionSystem::me()->GetAction("MoveRight")->AddListener(editor_player_controller); + LifeGame::ActionSystem::me()->GetAction("RotateYaw")->AddListener(editor_player_controller); + LifeGame::ActionSystem::me()->GetAction("RotatePitch")->AddListener(editor_player_controller); + return 0; +} + +uint32 LifeWorldEditor::Run(){ + while(true){ + game->GetInput()->Capture(); + game->processWorld(game_scene); + game->processWorld(editor_scene); + Sleep(1); + } + return 0; +} \ No newline at end of file diff --git a/LifeWorldEditor/LifeWorldEditor.h b/LifeWorldEditor/LifeWorldEditor.h new file mode 100644 index 0000000..84118b4 --- /dev/null +++ b/LifeWorldEditor/LifeWorldEditor.h @@ -0,0 +1,22 @@ +#ifndef LifeWorldEditor_h__ +#define LifeWorldEditor_h__ + +#include +#include + +class LifeWorldEditor +{ +public: + LifeWorldEditor(void); + ~LifeWorldEditor(void); + int32 Load(); + uint32 Run(); +private: + LifeGame::World* game_scene; + LifeGame::Game* game; + LifeGame::World* editor_scene; + LifeCore::IRenderWindow* main_window; + LifeGraphics::IRenderOutput* main_out; +}; + +#endif // LifeWorldEditor_h__ diff --git a/LifeWorldEditor/LifeWorldEditor.vcxproj b/LifeWorldEditor/LifeWorldEditor.vcxproj new file mode 100644 index 0000000..46a73b3 --- /dev/null +++ b/LifeWorldEditor/LifeWorldEditor.vcxproj @@ -0,0 +1,173 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {999B1AB0-CF3F-46EC-B0B5-164504EE3D6D} + Win32Proj + LifeWorldEditor + + + + Application + true + v120 + MultiByte + + + Application + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)$(ProjectName)\bin\ + $(SolutionDir)$(ProjectName)\tmp\$(Configuration)\ + $(ProjectName)_d + + + true + + + false + $(SolutionDir)$(ProjectName)\bin\ + $(SolutionDir)$(ProjectName)\tmp\$(Configuration)\ + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(REPO_DIR)\bullet-2.78\src;$(REPO_DIR)\ois-v1-3\includes;$(SolutionDir)\LifeGraphics\src;$(SolutionDir)\LifeCore\src;$(SolutionDir)\LifeSound\src;$(SolutionDir)\LifeGame\src;$(REPO_DIR)/angelscript/include;$(SolutionDir)\LifeGUI\src;%(AdditionalIncludeDirectories) + + + Console + true + $(DXSDK_DIR)\Lib\x86;$(SolutionDir)\LifeCore\lib;$(SolutionDir)\LifeGraphics\lib;$(SolutionDir)\LifeGUI\lib;$(SolutionDir)\LifeSound\lib;$(SolutionDir)\LifeGame\lib;$(REPO_DIR)\bullet-2.78\msvc\2008\lib\Debug;$(REPO_DIR)\ois-v1-3\lib\;%(AdditionalLibraryDirectories) + OIS_static_d.lib;LifeGUI_d.lib;LifeGame_d.lib;LifeGraphics_d.lib;LifeSound_d.lib;LifeCore_d.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(REPO_DIR)\bullet-2.78\src;$(REPO_DIR)\ois-v1-3\includes;$(SolutionDir)\LifeGraphics\include;$(SolutionDir)\LifeCore\include;$(SolutionDir)\LifeSound\include;$(SolutionDir)\LifeGame\include;$(REPO_DIR)/angelscript/include;$(SolutionDir)\LifeGUI\include;%(AdditionalIncludeDirectories) + + + Console + true + $(DXSDK_DIR)\Lib\x86;$(SolutionDir)\LifeCore\lib;$(SolutionDir)\LifeGraphics\lib;$(SolutionDir)\LifeGUI\lib;$(SolutionDir)\LifeSound\lib;$(SolutionDir)\LifeGame\lib;$(REPO_DIR)\bullet-2.78\msvc\2008\lib\Debug;%(AdditionalLibraryDirectories) + LifeGUI_d.lib;LifeGame_d.lib;LifeGraphics_d.lib;LifeSound_d.lib;LifeCore_d.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(REPO_DIR)\bullet-2.78\src;$(REPO_DIR)\ois-v1-3\includes;$(SolutionDir)\LifeGraphics\src;$(SolutionDir)\LifeCore\src;$(SolutionDir)\LifeSound\src;$(SolutionDir)\LifeGame\src;$(REPO_DIR)/angelscript/include;$(SolutionDir)\LifeGUI\src;%(AdditionalIncludeDirectories) + + + Console + true + true + true + $(DXSDK_DIR)\Lib\x86;$(SolutionDir)\LifeCore\lib;$(SolutionDir)\LifeEngine\lib;$(SolutionDir)\LifeSound\lib;%(AdditionalLibraryDirectories) + LifeEngine.lib;LifeSound.lib;LifeCore.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(SolutionDir)\LifeEngine\include;$(SolutionDir)\LifeCore\include;$(SolutionDir)\LifeSound\include;%(AdditionalIncludeDirectories) + + + Console + true + true + true + $(DXSDK_DIR)\Lib\x86;$(SolutionDir)\LifeCore\lib;$(SolutionDir)\LifeEngine\lib;$(SolutionDir)\LifeSound\lib;%(AdditionalLibraryDirectories) + LifeEngine.lib;LifeSound.lib;LifeCore.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LifeWorldEditor/main.cpp b/LifeWorldEditor/main.cpp new file mode 100644 index 0000000..1fc8aed --- /dev/null +++ b/LifeWorldEditor/main.cpp @@ -0,0 +1,7 @@ +#include "LifeWorldEditor.h" + +int main(int argc, char* argv[]){ + LifeWorldEditor* editor = new LifeWorldEditor(); + editor->Load(); + return editor->Run(); +} \ No newline at end of file diff --git a/MyGame/MyGame.vcxproj b/MyGame/MyGame.vcxproj new file mode 100644 index 0000000..a1a0807 --- /dev/null +++ b/MyGame/MyGame.vcxproj @@ -0,0 +1,171 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + {55D99FD8-2A69-48FD-A060-D04D5A4A705D} + Win32Proj + MyGame + <Project Location In Database> + <Source Control Database> + <Local Binding Root of Project> + Mercurial Source Control Package + $(VCTargetsPath11) + + + + StaticLibrary + true + MultiByte + v120 + + + StaticLibrary + true + MultiByte + v120 + + + StaticLibrary + false + true + MultiByte + v120 + + + StaticLibrary + false + true + MultiByte + v120 + + + + + + + + + + + + + + + + + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + $(REPO_DIR)/angelscript/include;.\include;$(REPO_DIR)\ois-v1-3\includes;$(REPO_DIR)\bullet-2.78\src;..\LifeEngine\include;..\LifeGame\include;$(SolutionDir)\LifeCore\include;%(AdditionalIncludeDirectories) + + + Windows + true + + + lib\Crysis_d.lib + LifeEngine_d.lib;LifeGame_d.lib;LifeCore_d.lib + $(REPO_DIR)\ois-v1-3\lib;$(REPO_DIR)\bullet-2.78\lib;..\LifeEngine\lib;..\LifeGame\lib;$(SolutionDir)\LifeCore\lib;%(AdditionalLibraryDirectories) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + $(REPO_DIR)/angelscript/include;.\include;$(REPO_DIR)\ois-v1-3\includes;$(REPO_DIR)\bullet-2.78\src;..\LifeEngine\include;..\LifeGame\include;$(SolutionDir)\LifeCore\include;%(AdditionalIncludeDirectories) + + + Windows + true + + + lib\Crysis_d.lib + LifeEngine_d.lib;LifeGame_d.lib;LifeCore_d.lib + $(REPO_DIR)\ois-v1-3\lib;$(REPO_DIR)\bullet-2.78\lib;..\LifeEngine\lib;..\LifeGame\lib;$(SolutionDir)\LifeCore\lib;%(AdditionalLibraryDirectories) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + $(REPO_DIR)/angelscript/include;.\include;$(REPO_DIR)\ois-v1-3\includes;$(REPO_DIR)\bullet-2.78\src;..\LifeEngine\include;..\LifeGame\include;$(SolutionDir)\LifeCore\include;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + + + lib\Crysis.lib + LifeEngine.lib;LifeGame.lib + $(REPO_DIR)\ois-v1-3\lib;$(REPO_DIR)\freetype-2.4.3\objs\win32\vc2008;$(REPO_DIR)\bullet-2.78\lib;..\LifeEngine\lib;..\LifeGame\lib;%(AdditionalLibraryDirectories) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + $(REPO_DIR)/angelscript/include;.\include;$(REPO_DIR)\ois-v1-3\includes;$(REPO_DIR)\bullet-2.78\src;..\LifeEngine\include;..\LifeGame\include;$(SolutionDir)\LifeCore\include;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + + + lib\Crysis.lib + LifeEngine.lib;LifeGame.lib + $(REPO_DIR)\ois-v1-3\lib;$(REPO_DIR)\freetype-2.4.3\objs\win32\vc2008;$(REPO_DIR)\bullet-2.78\lib;..\LifeEngine\lib;..\LifeGame\lib;%(AdditionalLibraryDirectories) + + + + + + \ No newline at end of file diff --git a/MyGame/include/MyGame.h b/MyGame/include/MyGame.h new file mode 100644 index 0000000..c708165 --- /dev/null +++ b/MyGame/include/MyGame.h @@ -0,0 +1,382 @@ +#ifndef MyGame_h__ +#define MyGame_h__ + +#include "Game.h" +#include "Actor.h" +#include +#include "InputSystemOIS.h" +#include "states/StateGame.h" +#include +#include "EntityFactory.h" +#include "gui/GUIButton.h" +#include "gui/GUIText.h" +#include "gui/GUITextInput.h" +#include "gui/GUIFont.h" + +#include "Spectator.h" + +static const Vec3f colors[6] = +{ + Vec3f(0,1,0), + Vec3f(1, 0, 1), + Vec3f(1, 0, 0), + Vec3f(0,0,1), + Vec3f(1,1,0), + Vec3f(1,1,1) +}; + +class MyGame : public Game +{ +public: + Light* l; + Light* l2; + Light* l3; + + MyGame() + { + } + ~MyGame() + { + } + void Load(int _mainWindow, IRenderWindow* _outWindow) + { + if(_mainWindow) + { + customWindow = true; + output = _outWindow; + mainWindow = _mainWindow; + } + + Video::Timer t; + + Camera cam; + cam.setPosition(Vec3f(0.0f,0.0f,0.0f)); + cam.setDirection(Vec3f(-100.0f,0.0f,0.0f)); + Matrix4x4 v = cam.getView(); + Vec3f vector(0.0f, 10.0f, 10.0f); + Vec3f rs; + Vec3TransformCoordinate(vector, v, rs); + + Range r(0x0410, 0x04ff); + // GUIFont* fnt = new GUIFont(&r,1); + //CLog::me()->LogToConsole(true); + //CLog::me()->Write(AS_STRING("Creating new game")); + + //SetResourcesPath(AS_STRING("C:\\development\\Programming\\Projects\\Data\\")); + + //IPakFileContentProvider pp( _text("C:\\Data.pak")); + //IPakFileContentProvider pp( _text("Data.pak")); + //String mp(_text("root")); + //ResourceManager::me()->MountContentProvider(&pp, mp); + + t.Start(); + if(!customWindow) + { + SetWindow(1200, 700); + mainWindow = output->GetWindowHandle(); + } + GetWindow()->SetCaption(STR("Crysis 4. DirectX11. over9000 fps")); + t.Stop(); + printf("Window creation: %f\n", t.getTimeElapsed()); + + t.Start(); + InitVideo(RT_DIRECTX_11); + t.Stop(); + printf("Video creation: %f\n", t.getTimeElapsed()); + + t.Start(); + InitInput(); + t.Stop(); + printf("Input creation: %f\n", t.getTimeElapsed()); + + gameWorld = new World(this, pGraphics->GetSceneManager()->GetActiveScene(), physicsWorld); + /*int modeCount = GetGraphics()->GetMainRenderer()->GetDisplayModes(&modes); + + for(int i = 0; i < modeCount; i++) + { + printf("Mode ID: %i\n", i); + printf("Mode width: %i\n", modes[i].width); + printf("Mode height: %i\n", modes[i].height); + printf("Mode rate: %i\n", modes[i].refreshRate); + }*/ + + t.Start(); + + int ci = 0; + + /*float fieldSize = 3.5f; + int cnt = 4; + Vec3f offset(-fieldSize/2.0f,1.05f, fieldSize / 2.0f); + float step = fieldSize / (float)cnt;*/ + + /*for(int z = 0; z < cnt; z++) + { + for(int x = 0; x < cnt; x++) + { + Light* l = pGraphics->GetResourceManager()->CreatePointLight(offset + Vec3f(step * (float)x, 0, -step * float(z)), 4.35f); + l->color = colors[ci]; + l->intensity = 2.0f; + l->specularPower = 28.0f; + pGraphics->GetSceneManager()->GetActiveScene()->PushNode(l); + + //Light* l = pGraphics->GetResourceManager()->CreateSpotLight(offset + Vec3f(step * (float)x, 0, -step * (float)z),offset + Vec3f(step * (float)x, -1.0f, -step * (float)z),5.0f,1.0f, 1.2f); + //l->color = colors[ci]; + //l->intensity = 2.0f; + //l->specularPower = 28.0f; + //l->castShadows = false; + //pGraphics->GetSceneManager()->GetActiveScene()->PushNode(l); + + ci++; + if(ci>=6) ci = 0; + } + }*/ + /* + + l2 = pGraphics->GetResourceManager()->CreatePointLight(Vec3f(0.5f,0.4f,0), 3.0f); + l2->color = colors[2]; + l2->intensity = 1.0f; + l2->specularPower = 28.0f; + l2->castShadows = true; + pGraphics->GetSceneManager()->GetActiveScene()->PushNode(l2); + + l3 = pGraphics->GetResourceManager()->CreatePointLight(Vec3f(1.5f,0.4f,0), 3.0f); + l3->color = colors[3]; + l3->intensity = 1.0f; + l3->specularPower = 28.0f; + l3->castShadows = true; + pGraphics->GetSceneManager()->GetActiveScene()->PushNode(l3); + */ + + l = pGraphics->GetResourceManager()->CreatePointLight(Vec3f(0.0f,0.4f,0), 3.0f); + l->color = colors[0]; + l->intensity = 1.0f; + l->specularPower = 28.0f; + l->castShadows = true; + pGraphics->GetSceneManager()->GetActiveScene()->PushNode(l); + + Light* l2 = pGraphics->GetResourceManager()->CreateSpotLight(Vec3f(0.2f,1.0f,0.0f),Vec3f(0.0f,0.5f,1.6f),5.0f,1.3f, 1.40f); + l2->color = Vec3f(1.0f,1.0f,1.0f); + l2->intensity = 1.0f; + l2->specularPower = 28.0f; + l2->castShadows = true; + pGraphics->GetSceneManager()->GetActiveScene()->PushNode(l2); + + t.Stop(); + printf("Light creation: %f\n", t.getTimeElapsed()); + + IMaterial* mat2 = new IMaterial(); + mat2->castShadows = false; + mat2->shininess = 0.3f; + mat2->specularPower = 20.0f; + + mat2->normalMapping = true; + mat2->diffuseMap = ResourceManager::me()->GetTexture("root/Data/Textures/Rock/rock.jpg"); + mat2->normalMap = ResourceManager::me()->GetTexture("root/Data/Textures/Rock/rockNormal.jpg"); + + Entity* floor = new Entity(); + IGraphicsEntity* floorVisual = new IGraphicsEntity(GetGraphics()); + + IMesh* mesh = ResourceManager::me()->GetMesh("root/Data/Objects/House_opt.vmf"); + IObjectMesh* go = new IObjectMesh(mesh); + + IMaterial* mat = new IMaterial(); + mat->castShadows = false; + mat->shininess = 1; + mat->specularPower = 50.0f; + + mat->normalMapping = true; + mat->diffuseMap = ResourceManager::me()->GetTexture("root/Data/Textures/basic_floor_s.BMP"); + mat->normalMap = ResourceManager::me()->GetTexture("root/Data/Textures/basic_floor_s_ddn.BMP"); + go->setMaterial(mat, 0); + + floorVisual->SetBody(go); + + floor->SetGraphicalEntity(floorVisual); + + IPhysicsEntity* pe = new IPhysicsEntity(floor); + pe->CreateRigidBodyPhysics(Vec3f(0, 0, 0), go->getBoundingBox(), 0, NULL); + floor->SetPhysicsEntity(pe); + + gameWorld->AddEntity(floor); + + float fieldSize = 3.5f; + int cnt = 4; + Vec3f offset(-fieldSize/2.0f,1.05f, fieldSize / 2.0f); + float step = fieldSize / (float)cnt; + + for(int z = 0; z < cnt; z++) + { + for(int x = 0; x < cnt; x++) + { + Entity* thing = new Entity(); + IGraphicsEntity* thingVisual = new IGraphicsEntity(GetGraphics()); + thingVisual->Load("root/Data/Objects/Tower_opt.vmf"); + thingVisual->GetBody()->setMaterial(mat2, 0); + thing->SetGraphicalEntity(thingVisual); + + IPhysicsEntity* pe2 = new IPhysicsEntity(thing); + pe2->CreateRigidBodyPhysics(Vec3f(0, 0, 0), thingVisual->GetBody()->getBoundingBox(), 0, NULL); + thing->SetPhysicsEntity(pe2); + gameWorld->AddEntity(thing); + + thing->GetTransform()->SetPosition(Vec3f((float)x * step - 0.8f,0.2f,(float)z*step -1.5f)); + } + } + + /* + Light* l = pGraphics->GetResourceManager()->CreateSpotLight(Vec3f(1.2f,2.2f,1.2f),Vec3f(1.4f,0.0f,1.4f),5.0f,1.3f, 1.99f); + l->color = Vec3f(1.0f,1.0f,1.0f); + l->intensity = 1.0f; + l->specularPower = 28.0f; + l->castShadows = true; + pGraphics->GetSceneManager()->GetActiveScene()->PushNode(l);*/ + + /*Light* l2 = pGraphics->GetResourceManager()->CreateSpotLight(Vec3f(-1.6f,2.2f,0.4f),Vec3f(-1.6f,0.0f,0.4f),4.0f,1.0f, 1.5f); + l2->color = Vec3f(0.0f,0.0f,1.0f); + l2->intensity = 2.0f; + l2->specularPower = 28.0f; + l2->castShadows = false; + pGraphics->GetSceneManager()->GetActiveScene()->PushNode(l2);*/ + + /*player = (Player*)EntityFactory::me()->BuildEntity(AS_STRING("root/Data/Entities/thing.xml"), this); + + player->GetTransform()->SetPosition(Vec3f(0.3f,0.5f,0.2f)); + player->GetTransform()->SetRotation(Vec3f(0.0f,1.0f,0.0f), 1.0f); + + player->GetGraphicalEntity()->pMesh->getMaterial(0)->shininess = 0.5f; + player->GetGraphicalEntity()->pMesh->getMaterial(0)->specularPower = 15.0f;*/ + + /*t.Start(); + Entity* hall = EntityFactory::me()->BuildEntity(AS_STRING("root/Data/Entities/level_0_p0.xml"), this); + + gameWorld->AddEntity(hall); + t.Stop(); + printf("Hall creation: %f\n", t.getTimeElapsed()); + + t.Start(); + Entity* crate = EntityFactory::me()->BuildEntity(AS_STRING("root/Data/Entities/crate_small.xml"), this); + crate->GetTransform()->SetPosition(Vec3f(-1.0f, 0.8f, 0.0f)); + gameWorld->AddEntity(crate); + t.Stop(); + printf("Crate creation: %f\n", t.getTimeElapsed()); + + t.Start(); + Entity* tv = EntityFactory::me()->BuildEntity(AS_STRING("root/Data/Entities/tv_flat.xml"), this); + tv->GetTransform()->SetPosition(Vec3f(-4.0f, 1.6f, -1.4f)); + tv->GetTransform()->SetRotation(Vec3f(0.0f,1.0f,0.0f), 3.1415926535f); + gameWorld->AddEntity(tv); + t.Stop(); + printf("TV creation: %f\n", t.getTimeElapsed());*/ + + //miranda->Save(); + StateGame* gs = new StateGame(AS_STRING("gameplay"), this); + stateManager.AddState(gs->GetName(), gs); + + StateSettings* sm = new StateSettings("settings_menu", this); + stateManager.AddState(sm->GetName(), sm); + + actionMap.MapAction(ACTION_FORWARD, KEY_W); + actionMap.MapAction(ACTION_BACKWARD, KEY_S); + actionMap.MapAction(ACTION_LEFT, KEY_A); + actionMap.MapAction(ACTION_RIGHT, KEY_D); + actionMap.MapAction(ACTION_JUMP, KEY_SPACE); + actionMap.MapAction(ACTION_ACCELERATION, KEY_LSHIFT); + actionMap.MapAction(ACTION_FIRE, KEY_MOUSE_LEFT); + actionMap.MapAction(ACTION_USE, KEY_F); + actionMap.MapAction(ACTION_TURNLR, KEY_AXIS_X); + actionMap.MapAction(ACTION_TURNUD, KEY_AXIS_Y); + + //Spectator* sp = new Spectator(this); + + //sp->GetTransform()->SetPosition(Vec3f(0,1,0)); + Player* p = new Player(this); + + gameWorld->AddEntity(p); + + SetPlayer(p); + + /* GUIManager* gui = GetGraphics()->GetGUI(); + GUIManager::me()->AddOverlay(AS_STRING("main_menu")); + GUIOverlay* mainMenu = GUIManager::me()->GetOverlay(AS_STRING("main_menu")); + + //add button + GUIButton* button = new GUIButton(gui); + button->SetPosition(Vec2i(50,50)); + button->SetWidth(200); + button->SetHeight(50); + button->SetBackColor(Vec4f(1.0f,0.0f,0.0f,0.99f)); + mainMenu->AddChild(button); + + //add button + GUIButton* button2 = new GUIButton(gui); + button2->SetPosition(Vec2i(50,110)); + button2->SetWidth(200); + button2->SetHeight(50); + button2->SetBackColor(Vec4f(0.0f,1.0f,0.0f,0.99f)); + mainMenu->AddChild(button2); + + //add button + GUIButton* button3 = new GUIButton(gui); + button3->SetPosition(Vec2i(50,170)); + button3->SetWidth(200); + button3->SetHeight(50); + button3->SetBackColor(Vec4f(0.0f,0.0f,1.0f,0.99f)); + mainMenu->AddChild(button3); + + button->Event_MouseEnter+=NewDelegate(this, &CrysisGame::BT1Entered); + button->Event_MouseLeave+=NewDelegate(this, &CrysisGame::BT1Leaved); + button->Event_MouseClick+=NewDelegate(this, &CrysisGame::BT1Click); + + button->Event_MouseRightClick+=NewDelegate(this, &CrysisGame::BT1RightClick); + button->Event_MouseMiddleClick+=NewDelegate(this, &CrysisGame::BT1MiddleClick); + + GUITextInput* ti = new GUITextInput(gui); + ti->SetPosition(Vec2i(320,50)); + mainMenu->AddChild(ti); + + GUITextInput* ti2 = new GUITextInput(gui); + ti2->SetPosition(Vec2i(320,100)); + mainMenu->AddChild(ti2); + //add text + GUIText* text = new GUIText(gui); + mainMenu->AddChild(text); + text->SetPosition(Vec2i(50,5)); + text->SetWidth(200); + text->SetText(STR("Ýòî êíîïêè! ÊÝÝÝÏ")); + text->SetBackColor(Vec4f(0.2f,0.2f,0.3f,0.9f));; + text->SetHAlign(HA_LEFT); + text->SetVAlign(VA_CENTER); + + //add text + GUIText* text2 = new GUIText(gui); + mainMenu->AddChild(text2); + text2->SetPosition(Vec2i(320,5)); + text2->SetWidth(200); + text2->SetText(STR("EditBox-û!")); + text2->SetBackColor(Vec4f(0.2f,0.2f,0.3f,0.9f)); + text2->SetHAlign(HA_LEFT); + text2->SetVAlign(VA_CENTER);*/ + } + void DoMainLoop() + { + bRunning = true; + + while(bRunning) + { + //l = pGraphics->GetResourceManager()->CreatePointLight(Vec3f(-0.5f,0.4f,0), 3.0f); + /*l->intensity = cosf(GetGraphics()->appTime) * 0.5f + 1.2f; + l->getTransform()->SetPosition(Vec3f(sinf(GetGraphics()->appTime) * 0.5f, 0.4f, cosf(GetGraphics()->appTime) * 0.5f)); + //l2 = pGraphics->GetResourceManager()->CreatePointLight(Vec3f(0.5f,0.4f,0), 3.0f); + + l2->intensity = sinf(GetGraphics()->appTime + 0.25f) * 0.5f + 1.2f; + l2->getTransform()->SetPosition(Vec3f(0.5f, 0.4f, cosf(GetGraphics()->appTime) * 1.5f)); + //l3 = pGraphics->GetResourceManager()->CreatePointLight(Vec3f(1.5f,0.4f,0), 3.0f); + + l3->intensity = cosf(GetGraphics()->appTime + 0.75f) * 0.5f + 1.2f; + l3->getTransform()->SetPosition(Vec3f(1.5f, 0.4f, sinf(GetGraphics()->appTime) * 1.5f));*/ + + this->process(); + } + } +}; +#endif // MyGame_h__ \ No newline at end of file diff --git a/MyGame/include/states/StateGame.h b/MyGame/include/states/StateGame.h new file mode 100644 index 0000000..383e4d9 --- /dev/null +++ b/MyGame/include/states/StateGame.h @@ -0,0 +1,27 @@ +#ifndef StateGame_h__ +#define StateGame_h__ + +#include "IGameState.h" +#include "ActionBuffer.h" +class MyGame; + +class StateGame : public IGameState +{ +public: + StateGame(const String& name, MyGame* engine); + void Update(); + void Render(); + void OnEnter(); + void OnLeave(); + void ProcessCmd(const String& cmd, void* params); + + bool KeyDown( Core::Key key, unsigned int text, const std::string& str ); + bool KeyUp( Core::Key key, unsigned int text, const std::string& str ); + bool MouseMove( int absX, int relX, int absY, int relY); + bool MousePressed(int buttonId); + bool MouseReleased(int buttonId); +private: + MyGame* pGame; + ActionBuffer playerActions; +}; +#endif // StateGame_h__ \ No newline at end of file diff --git a/MyGame/include/states/StateSettings.h b/MyGame/include/states/StateSettings.h new file mode 100644 index 0000000..df6ebb7 --- /dev/null +++ b/MyGame/include/states/StateSettings.h @@ -0,0 +1,33 @@ +#ifndef StateSettings_h__ +#define StateSettings_h__ + +#include "IGameState.h" +#include "ActionBuffer.h" +#include +#include +#include +#include + +class MyGame; + +class StateSettings : public IGameState +{ +public: + StateSettings(const String& name, MyGame* engine); + void Update(); + void Render(); + void OnEnter(); + void OnLeave(); + void ProcessCmd(const String& cmd, void* params); + + bool KeyDown( Key key, unsigned int text, const std::string& str ); + bool KeyUp( Key key, unsigned int text, const std::string& str ); + bool MouseMove( int absX, int relX, int absY, int relY); + bool MousePressed(int buttonId); + bool MouseReleased(int buttonId); + +private: + MyGame* pGame; + GUIOverlay* pMenu; +}; +#endif // StateSettings_h__ \ No newline at end of file diff --git a/MyGame/src/states/StateGame.cpp b/MyGame/src/states/StateGame.cpp new file mode 100644 index 0000000..5f8bb99 --- /dev/null +++ b/MyGame/src/states/StateGame.cpp @@ -0,0 +1,156 @@ +#include "states/StateGame.h" +#include "MyGame.h" + +StateGame::StateGame(const String& name, MyGame* engine) : IGameState(name) +{ + pGame = engine; + pGame->GetInput()->AddListener(pGame->GetGraphics()->GetGUI()); +} +void StateGame::Update() +{ + playerActions.Reset(); + //grab input actions + + int num = pGame->GetActionMap()->Size(); + pGame->GetActionMap()->ResetIterator(); + for(int i = 0; i< num; i++) + { + Key k = pGame->GetActionMap()->CurrentKey(); + GameAction a = pGame->GetActionMap()->CurrentAction(); + + if(k == KEY_WHEEL_UP | k == KEY_WHEEL_DOWN) + { + int state = pGame->GetInput()->GetMouseWheel(); + if(state > 0 && k == KEY_WHEEL_UP) + { + playerActions.AddAction(a); + } + else if(state < 0 && k == KEY_WHEEL_DOWN) + { + playerActions.AddAction(a); + } + } + else if(k == KEY_AXIS_X) + { + int i = pGame->GetInput()->GetMouseRelativeX(); + if(i) + playerActions.AddAction(a); + } + else if(k == KEY_AXIS_Y) + { + int i = pGame->GetInput()->GetMouseRelativeY(); + if(i) + playerActions.AddAction(a); + } + else if(k > KEY_MOUSE_OFFSET) + { + if(pGame->GetInput()->MouseButtonPressed(k)) + playerActions.AddAction(a); + } + else + { + if(pGame->GetInput()->KeyPressed(k)) + { + playerActions.AddAction(a); + } + } + pGame->GetActionMap()->MoveNext(); + } + + if(pGame->player) + pGame->player->ProcessActions(&playerActions); + + float moveSpeed = 7.0f; + if(pGame->GetInput()->KeyPressed(KEY_ESCAPE)) + { + pGame->Exit(); + } + + pGame->GetWorld()->Update(pGame->GetGraphics()->GetTimeElapsed()); + pGame->GetGraphics()->GetGUI()->Update(pGame->GetGraphics()->GetTimeElapsed()); + pGame->GetPhysics()->Tick(pGame->GetGraphics()->GetTimeElapsed()); + pGame->GetGraphics()->GetSceneManager()->GetActiveScene()->Update(pGame->GetGraphics()->GetTimeElapsed()); + /*if(pGame->GetInput()->KeyPressed(KEY_W)) + { + Vec3f pos = pGame->cam.getPosition(); + Vec3f dir = pGame->cam.getDirection(); + pos += dir * pGame->GetGraphics()->getTimeElapsed() * moveSpeed; + pGame->cam.setPosition(pos); + } + if(pGame->GetInput()->KeyPressed(KEY_S)) + { + Vec3f pos = pGame->cam.getPosition(); + Vec3f dir = pGame->cam.getDirection(); + pos += dir * -pGame->GetGraphics()->getTimeElapsed() * moveSpeed; + pGame->cam.setPosition(pos); + } + if(pGame->GetInput()->KeyPressed(KEY_A)) + { + Vec3f pos = pGame->cam.getPosition(); + Vec3f dir = pGame->cam.getRight(); + pos += dir * -pGame->GetGraphics()->getTimeElapsed()* moveSpeed; + pGame->cam.setPosition(pos); + } + if(pGame->GetInput()->KeyPressed(KEY_D)) + { + Vec3f pos = pGame->cam.getPosition(); + Vec3f dir = pGame->cam.getRight(); + pos += dir * pGame->GetGraphics()->getTimeElapsed()* moveSpeed; + pGame->cam.setPosition(pos); + } + + if(pGame->GetInput()->GetMouseRelativeX() != 0) + { + pGame->cam.Yaw((float)pGame->GetInput()->GetMouseRelativeX() / 300.0f); + } + if(pGame->GetInput()->GetMouseRelativeY() != 0) + { + pGame->cam.Pitch((float)pGame->GetInput()->GetMouseRelativeY() / 300.0f); + }*/ +} + +bool StateGame::KeyDown( Key key, unsigned int text, const std::string& str ) +{ + return true; +} +bool StateGame::KeyUp( Key key, unsigned int text, const std::string& str ) +{ + if(key == KEY_M) + { + pGame->GetStateManager()->SelectState(AS_STRING("settings_menu")); + } + return true; +} +bool StateGame::MouseMove( int absX, int relX, int absY, int relY) +{ + return true; +} +bool StateGame::MousePressed(int buttonId) +{ + return true; +} +bool StateGame::MouseReleased(int buttonId) +{ + return true; +} + +void StateGame::Render() +{ + // + // + pGame->GetGraphics()->GetSceneManager()->GetActiveScene()->Render(); + + //render physics debug info + pGame->DrawDebug(); + + //pGame->GetGraphics()->GetGUI()->Render(); +} +void StateGame::ProcessCmd(const String& cmd, void* params) +{ +} +void StateGame::OnEnter() +{ +} +void StateGame::OnLeave() +{ +} \ No newline at end of file diff --git a/MyGame/src/states/StateSettings.cpp b/MyGame/src/states/StateSettings.cpp new file mode 100644 index 0000000..6809e14 --- /dev/null +++ b/MyGame/src/states/StateSettings.cpp @@ -0,0 +1,70 @@ +#include "states/StateSettings.h" +#include "MyGame.h" + +StateSettings::StateSettings(const String& name, MyGame* engine) : IGameState(name) +{ + pGame = engine; + + pGame->GetGraphics()->GetGUI()->AddOverlay(AS_STRING("settings_menu")); + pMenu = pGame->GetGraphics()->GetGUI()->GetOverlay(AS_STRING("settings_menu")); + pMenu->drawBackground = true; + + /*GUIButton* button2 = new GUIButton(pGame->GetGraphics()->GetGUI()); + button2->SetPosition(Vec2i(50,110)); + button2->SetWidth(200); + button2->SetHeight(50); + button2->SetBackColor(Vec4f(0.0f,1.0f,0.0f,0.99f)); + pMenu->AddChild(button2);*/ + + GUIComboBox* res = new GUIComboBox(pGame->GetGraphics()->GetGUI()); + res->SetPosition(Vec2i(100,10)); + pMenu->AddChild(res); + + /* GUIPlane* plane = new GUIPlane(pGame->GetGraphics()->GetGUI()); + plane->SetBorderEnabled(true); + plane->SetWidth(300); + pMenu->AddChild(plane);*/ +} +void StateSettings::Update() +{ + pGame->GetGraphics()->GetGUI()->Update(pGame->GetGraphics()->GetTimeElapsed()); +} +void StateSettings::Render() +{ + pGame->GetGraphics()->GetGUI()->Render(); +} +void StateSettings::OnEnter() +{ + pGame->GetGraphics()->GetGUI()->SelectOverlay(AS_STRING("settings_menu")); +} +void StateSettings::OnLeave() +{ +} +void StateSettings::ProcessCmd(const String& cmd, void* params) +{ +} + +bool StateSettings::KeyDown( Key key, unsigned int text, const std::string& str ) +{ + return true; +} +bool StateSettings::KeyUp( Key key, unsigned int text, const std::string& str ) +{ + if(key == KEY_M) + { + pGame->GetStateManager()->SelectState(AS_STRING("gameplay")); + } + return true; +} +bool StateSettings::MouseMove( int absX, int relX, int absY, int relY) +{ + return true; +} +bool StateSettings::MousePressed(int buttonId) +{ + return true; +} +bool StateSettings::MouseReleased(int buttonId) +{ + return true; +} \ No newline at end of file diff --git a/README.md b/README.md index ccf4986..2f1f85f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1 @@ -# LifeProject - +# LifeProject \ No newline at end of file diff --git a/TestBed/TestBed.cpp b/TestBed/TestBed.cpp new file mode 100644 index 0000000..e1a8f34 --- /dev/null +++ b/TestBed/TestBed.cpp @@ -0,0 +1,96 @@ +#include +#include "log/NLog.h" +#include "InputSystem.h" +#include "IInputSystem.h" +#include "media/WebCamera.h" +#include +#include +#include "WindowWin.h" +#include "WindowPixelManager.h" + +using namespace LifeCore; + +static WindowWin* wnd; +static WindowPixelManager* mgr; + + +static void OnImageCaptured(WebCamera* source, RawBuffer* image) { + LogInfo() << "Image captured!"; +} + +uint8 BgrToLum(uint8* bgr) { + //0.21 R + 0.71 G + 0.07 B. + float b = (float(bgr[0]) / 255.0f) * 0.07f; + float g = (float(bgr[1]) / 255.0f) * 0.71f; + float r = (float(bgr[2]) / 255.0f) * 0.21f; + return uint8((b + g + r) * 255.0f); +} + +void SetChannel(RawBuffer* image, int id, uint8 val) { + for(int i = 0; i < image->Type.MF_MT_FRAME_SIZE; ++i) { + int offset = i * 3; + image->data[offset + id] = val; + } +} + +int main() +{ + HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + wnd = new WindowWin(); + wnd->SetSize(1024, 768); + wnd->CenterWindow(); + mgr = new WindowPixelManager(wnd, 24); + auto cameras = WebCamera::GetConnectedDevices(); + for(auto cam : cameras) { + std::wcout << cam->GetName() << std::endl; + } + + WebCamera* cam = new WebCamera(cameras[0]); + cam->Event_ImageCapture.Connect(OnImageCaptured); + + + + + while(true) { + LogInfo() << "Opening"; + cam->Open(); + + auto format = cam->GetImage()->Type; + int imgSize = format.MF_MT_FRAME_SIZE * 3; + RawBuffer* lastImage = new RawBuffer(imgSize, format); + RawBuffer* diffImage = new RawBuffer(imgSize, format); + + int frameId = -1; + while(frameId < 14000) { + //cam->Update(); + if(cam->FrameNumber != frameId) { + HWND handle = (HWND)wnd->GetWindowHandle(); + auto image = cam->GetImage(); + + //make diff + for(int i = 0; i < imgSize; i+=3) { + uint8 lum = BgrToLum(image->data + i); + + diffImage->data[i + 0] = lum; + diffImage->data[i + 1] = lum; + diffImage->data[i + 2] = lum; + } + + + + mgr->SetPixels(diffImage->Type.width, diffImage->Type.height, diffImage->data); + frameId = cam->FrameNumber; + LogInfo() << "Frame id " << frameId; + } + wnd->Update(); + Sleep(1); + } + cam->Close(); + Sleep(500); + LogInfo() << "Iteration"; + } + + std::cin.get(); + return 0; +} \ No newline at end of file diff --git a/TestBed/TestBed.vcxproj b/TestBed/TestBed.vcxproj new file mode 100644 index 0000000..b9aec0c --- /dev/null +++ b/TestBed/TestBed.vcxproj @@ -0,0 +1,162 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {B7947426-0E53-43A2-9F19-483BF7D205CE} + Win32Proj + TestBed + <Project Location In Database> + <Source Control Database> + <Local Binding Root of Project> + Mercurial Source Control Package + $(VCTargetsPath11) + + + + Application + true + MultiByte + v120 + + + Application + true + Unicode + v120 + + + Application + false + true + Unicode + v120 + + + Application + false + true + Unicode + v120 + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + C:\nettlemath\nettlemath;$(SolutionDir)\LifeCore\ + + + Console + true + $(SolutionDir)\bin + LifeCore_d.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)\LifeCore\ + + + Console + true + $(SolutionDir)\bin + LifeCore_d.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)\LifeCore\ + + + Console + true + true + true + $(SolutionDir)\bin + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)\LifeCore\ + + + Console + true + true + true + $(SolutionDir)\bin + + + + + + + + + \ No newline at end of file

    CW3?Lc7GskECG42@<5*1yqye9^sRL#T)1cW ze#ZpCh3~gW-fJcyjGO(4n5F~}BI&J(v6-GWnMog>zHM6R88^lwu~>@N53ggRBp9N@ zdaeowalZYH9y&2{c?BBjtTvC&w=b*Z>OY;|SO0l~cja|s`DDvg>wXvDyBy(3nNEcH zOu1JaPkd2EH^VP`3)Ysy^VD+a%^!d(hnoEXxN>OaAAl=|68{0Xa_IaYfGfup;RA4G zX%L6!$kKN|1VhF-_5nCTj8FbR!MhI}EPFXrjHW^k7Nde0gp~TgGnyjast*{W%D%a) zJZKKeUFCsuQ0^)Zo`Z5%c>o=hyUK&;pxl)nNQdOC^k6zLVkJUrl|7sUvZ3Jf;1G-jo(%_KE$o~)2y0TG&}~5Z1!Z zm4mQWea0Mut?KjUKwMRwJqKY6(5}n(hkI{UciUkd?M^~4n={e(e%iI;4ffNjH3j$9 zs4>I#)T)|5@;Mm2p-eGnC>~JuASGo)P}KE7pTc%3-++DHY}TvWr*cy*+||owaet|s zX*rYwjx$Q$kmIP|O+$`@dp8X^j^W)jq*1Lt4=)GP{%kg*7nSYP_3hJlM}F>|Fk$Uq zyK2MOu6EN%7MEqtaVzGH7HUM$TQn{E7^2Q{NK!N<$I#R!RFHp7O2wd)=;#nD;$->u z`t7s#;Z)rneEWLe>D&N9$fuC(yj6Q1;Dp%W)JFRS|pAY-k{mEW$2)yyDA>4hUF<3QpfMC>7mQwQwjzz-;YNxl)4VeNMNDqtwh zXoS*Mob-`h?{)P|S>KB3MrNHT(<>UsRmai8T;O3zETXf0w#c^I%jNL??eX#N43O09 lH}a}evLLezK&F<Project "E:\pugixml-1.0\scripts\pugixml_vs2010_static.vcxproj" on node 2 (rebuild target(s)). + 1>InitializeBuildStatus: + Creating "vs2010\x32\Release\pugixml.unsuccessfulbuild" because "AlwaysCreate" was specified. + ClCompile: + C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\CL.exe /c /Zi /nologo /W3 /WX- /Ox /Oy- /D NDEBUG /D _MBCS /GF /Gm- /EHsc /MT /GS /Gy /fp:precise /Zc:wchar_t /Zc:forScope /Fo"vs2010\x32\Release\\" /Fd"vs2010\x32\pugixml_s.pdb" /Gd /TP /analyze- /errorReport:prompt ..\src\pugixml.cpp + pugixml.cpp + Lib: + C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\Lib.exe /OUT:"vs2010\x32\pugixml_s.lib" /NOLOGO vs2010\x32\Release\pugixml.obj + pugixml_vs2010_static.vcxproj -> E:\pugixml-1.0\scripts\vs2010\x32\pugixml_s.lib + FinalizeBuildStatus: + Deleting file "vs2010\x32\Release\pugixml.unsuccessfulbuild". + Touching "vs2010\x32\Release\pugixml.lastbuildstate". + 1>Done Building Project "E:\pugixml-1.0\scripts\pugixml_vs2010_static.vcxproj" (rebuild target(s)). + +Build succeeded. + +Time Elapsed 00:00:03.27 diff --git a/LifeGraphics/pugixml/scripts/vs2010/x32/pugixml_s.lib b/LifeGraphics/pugixml/scripts/vs2010/x32/pugixml_s.lib new file mode 100644 index 0000000000000000000000000000000000000000..5f7922defaec91d37a3ae7942b4ee7ed6fe0f139 GIT binary patch literal 1025068 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 z%0D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 z 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/bRo$Cao)rYm2nk7)`^ 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

L9<6o7KWpF&D641!qRiuXd*R|7}lBt zgIeB1yR#(}l)vUcxL{a?yw;i33RXU0GzX%Eq@kyt(I(=#5iQ%947Fw2GrKY^E$Q}9 zqAjsR5>pfx75ep5K4<1KR&Y=^3kf4;71DYxD3>s-<)Ls4gm2JOG{6@#D`mp;f4NtELv zCg~r?dQvVsTVg8Mjs2s{Ie{s+mgMH_oO@{{>e32j8TvH?H)5bAo@=Kp_ zRz1Z8&;!azQ4quUaSey1e@TfK(5l3Hz8?U6(xuJyOP{`qwwhoDqk1-*NE#Z1!3;*m zEiG&6N!`$`iC~nHYT`*sYwD8~El;_LS-HH%4J?=OKj|uAycyzOPh#0Q2)$?1u5GF< zI}8cu3`0v%dr5K;StJjxfg-$$e+2r*T0!w`F7MM1Mns1gf+SOtbZF72Fk}Q1MEo)Y zNlHPsDCrjJLoQ-o`Rm3w4Kg@_u{pfKn6R82ZiD<$o|zB$l|SYW`y>icKSfLzvnXhOEKws(~Wxy|e@~W|f{?E{#z!}ma z8W>#~)c#4EPG~0r1{JJ&k8Mv>2J&Qap&+hvd7Klxdz0W%-qxLYHyBf!$;zRa45w z&~*FOuEJj1t>Oi(L<+p&nQ8Jwa3EEHqY6cD%;j0pL=_r!75Xc6FOwM(@4;%DiMF60 zoHHO@x|I9`B#ZLTsKm~Vh3Ld>9APhVRUuvyyOF7OK{FzvO|%2Iqt=R8B8=J&^tH=c zH|inTy9>2eWVRd?LdM&L{s_jOydnPR#|e%??|2Vz7%R%+OmpHyNh{p!*gt7}Ty{k+ zx5VEDJXb-#*Z$7#f7~l6o^(eI`Tv8MLfL=%8|L$Q?3C7L#!@NnYmjXV{Gu=7$YPJk z{GWS&2y?P5Uft|1xBczGyeXXas57&Br&07VCN1X)LpAW9Yo!k+l%ub5fgW&%_a3uB z(4kL#t!4l8v$Py)k2aq(*!3`;(toI)*{BNv`IGk&^yz--^RDDYCwLoxk39wrq_6Ux z@gVf95-|VZeF^V9{L*j6*mKUrVeFn6m??dr22Jy>!WmeiK>j7^GM@KK-z|SZ==TYa z-5HS4m)q)4TI{(*@Bf1E>fKV- zn0a+$L>iQ5tnO3)`#NmBZ-afVw3fZ|zLpsq?3W+&e=hDd_~!+q{w`^sb#WZ$8OAvd z%adCwZz{c~Ju@@8NQjBe%dd4co%($L`5)e&T=U-Hm3`vwFE<$fHf$PrbL!Q9CNG|e za9p4Gb?ugKKistG=}&&Sd~$1Yl#UorFI~8LlxH#~}Cg{Pc!5-gFn!RW08C(4W(% z`n5k#VC-+s1c%^HNi`v3$Mkf}W80N-nWsGg?60It+5~6XvW46J?T4pHOWIlN*}!Ug zrAJ~U%NwCeFjxAtIw@q^^gqlv8E^GD{3z#l)w)#v$y-pr`)|Y8ea-|1zHS`HJ{cD> z(>jW}{f&cJS`d9HDH$pfWt2M?d8?M5v&@nO*0U`3^Xb=ryfSp)kR z4(`X^)BJjx&Ko?u`qHbtu?OG(>c~TWU-CvVgVhpHa2Szk1=nBaqb{_zy#3w(i literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/scripts/pugixml_vs2010_static.vcxproj b/LifeGraphics/pugixml/scripts/pugixml_vs2010_static.vcxproj new file mode 100644 index 0000000..c111373 --- /dev/null +++ b/LifeGraphics/pugixml/scripts/pugixml_vs2010_static.vcxproj @@ -0,0 +1,163 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + pugixml + {89A1E353-E2DC-495C-B403-742BE206ACED} + pugixml + Win32Proj + + + + StaticLibrary + MultiByte + + + StaticLibrary + MultiByte + + + StaticLibrary + MultiByte + + + StaticLibrary + MultiByte + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + vs2010\x32\ + vs2010\x32\Debug\ + vs2010\x64\ + vs2010\x64\Debug\ + vs2010\x32\ + vs2010\x32\Release\ + vs2010\x64\ + vs2010\x64\Release\ + $(ProjectName)_sd + $(ProjectName)_sd + $(ProjectName)_s + $(ProjectName)_s + + + + Disabled + _DEBUG;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + true + + + Level3 + $(OutDir)pugixml_sd.pdb + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + + + + + + X64 + + + Disabled + _DEBUG;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + true + + + Level3 + $(OutDir)pugixml_sd.pdb + ProgramDatabase + + + _DEBUG;%(PreprocessorDefinitions) + + + + + + Full + NDEBUG;%(PreprocessorDefinitions) + true + MultiThreaded + true + + + Level3 + $(OutDir)pugixml_s.pdb + ProgramDatabase + + + NDEBUG;%(PreprocessorDefinitions) + + + + + + X64 + + + Full + NDEBUG;%(PreprocessorDefinitions) + true + MultiThreaded + true + + + Level3 + $(OutDir)pugixml_s.pdb + ProgramDatabase + + + NDEBUG;%(PreprocessorDefinitions) + + + + + + + + + + + + + + diff --git a/LifeGraphics/pugixml/scripts/pugixml_vs2010_static.vcxproj.user b/LifeGraphics/pugixml/scripts/pugixml_vs2010_static.vcxproj.user new file mode 100644 index 0000000..ace9a86 --- /dev/null +++ b/LifeGraphics/pugixml/scripts/pugixml_vs2010_static.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/LifeGraphics/pugixml/scripts/vs2010/x32/Debug/CL.read.1.tlog b/LifeGraphics/pugixml/scripts/vs2010/x32/Debug/CL.read.1.tlog new file mode 100644 index 0000000000000000000000000000000000000000..330372f729ea886bab4cd21e56fd8bc545155677 GIT binary patch literal 9398 zcmd6t%Wm5+5JhKQp#P8`s8KhI0^Ru`J1(pTBso@!p!xrwXnQVcRYJ0AfchFjAd2SW za^}u(ILz-~Kck3m@f=N@VjqVXqKspFie)U~Gxc4xv3c|Rg1Uz1V|<{b{%87Dv5lHi z$M>F5>R88$_aT1p?s?a=(U>)(>bs)d^t}bkj+Q6-^vqxJ?q+i|^s4F6(x!|%+8$_8 zr1?t9oAkRz-_idr9;xdXO&IPd^^~{N)qE{kU0&g1THl&_VN?A*&oi}0+8=1OrF5L( zc)ivxp3>+AsP5wneOta)AgkixUHy9`)~1!co&EnvVS`jk9%<++D>$&~f_KSE8p>r~A)7y1dcbjY*iXwfI=!mivX^0m+G0`HG<}vCum?fT@ z8^9pHeXX3r>vQ99i6$!Y3N9V!96Rlzt_XMZ46ga7J=}{RM^qjRbAPagk**BF03T(O z$`vY#@JM5{O~w2!44aF8%+bC_4%ss187>cwoZA{tJ}wN3p_CJC$)$KOR3JE;<{t10 ziX`QwdYc4p0YmmsJ2bI1>0-m&ppNzv>oG1C>#`6H<(yp#Kk zwIOm)mP|N2%p4LIme|L1hvh;dAEEon`Q7L>b1?(j7!fRO`jAF$$4*BqimP^T#Kn!A zOTH}>&b-K|7axYKksR~;Pz>q*OJ}gXiJSBPV0dA7ZFv;+dMrh^ltQ3eN)Li*ejh8k WOCDKST-O~~&Jp;}qe3v{aQ^^@S`%Xc literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/scripts/vs2010/x32/Debug/CL.write.1.tlog b/LifeGraphics/pugixml/scripts/vs2010/x32/Debug/CL.write.1.tlog new file mode 100644 index 0000000000000000000000000000000000000000..e35bffe79d23eb74d17dced57725f9c774d6c3cc GIT binary patch literal 274 zcmezWFOI>L!HOY8%n-!jj3KWF6bk@CUIs3L z`kjF$cmmai0L_YF2m_KvK$8u@rbjRs19>qFE{i5`PLP{xaDHlg}69_Shu MpicvUq!ZW<08WG_LjV8( literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/scripts/vs2010/x32/Debug/Lib-link.read.1.tlog b/LifeGraphics/pugixml/scripts/vs2010/x32/Debug/Lib-link.read.1.tlog new file mode 100644 index 0000000..46b134b --- /dev/null +++ b/LifeGraphics/pugixml/scripts/vs2010/x32/Debug/Lib-link.read.1.tlog @@ -0,0 +1 @@ +ÿþ \ No newline at end of file diff --git a/LifeGraphics/pugixml/scripts/vs2010/x32/Debug/Lib-link.write.1.tlog b/LifeGraphics/pugixml/scripts/vs2010/x32/Debug/Lib-link.write.1.tlog new file mode 100644 index 0000000000000000000000000000000000000000..2f97762acee7399e6d974dfdf5c3bcc11d35f06b GIT binary patch literal 210 zcmb`BI|_h65JO)rcofA?XSuG3qA32bSoHpvP{Vdw+YFfml6PMR6$Orwm70c~o{or= wm_+_e$s~Vdk#=mlnUd6}JGnlGRg3Wr-=>&BNlQrZ%hH?k6pa;WCqm;l9+C$hnE(I) literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/scripts/vs2010/x32/Debug/Lib.read.1.tlog b/LifeGraphics/pugixml/scripts/vs2010/x32/Debug/Lib.read.1.tlog new file mode 100644 index 0000000000000000000000000000000000000000..7e4f76ea00ce525ff7449f66e7bba9c20d041227 GIT binary patch literal 378 zcmchT!3x4K5JcxJ_$d~vH*adSRNAC8sa6U8|5vDQgCKbD=q0nWyEC)o`MB$#jgoqe zs?}&xr&bGViCpTy@FOGW$~>a#-WNxb26#YSp>DJKe}C}; DRERdU literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/scripts/vs2010/x32/Debug/cl.command.1.tlog b/LifeGraphics/pugixml/scripts/vs2010/x32/Debug/cl.command.1.tlog new file mode 100644 index 0000000000000000000000000000000000000000..476368da20ffd6659bc77f2e8ca2433024c25c9c GIT binary patch literal 556 zcmbu6PfNo<5XIkF@HMxT;1o0%xW_I7c zc{6YF`SGB>x|(XJJH@)vSc$H6s!)N>z;BhR_v<|X8?ugdq_∋X6@oaigsY_9MH- z?{)6u7~LAV(aOilBN6wRB6_*7K5ABjB_KVzsF7IDp5ND{cD`<^5sc6n3Fk&T{?4iF zWi5wKr&1YtIoX-v6K-9-@qN;q*O~S|@HHMvl$J#2GX|VndPSkbghHFr0goM~6v0{O z1@BW<%ajIm^cUY5H`f7Y1+{l@11hahX|iMQXT-GY8O*g|7pnA5t!ADXJ(kvK3g+yz O_inc+JM;A4|NI5IBve-b literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/scripts/vs2010/x32/Debug/lib.command.1.tlog b/LifeGraphics/pugixml/scripts/vs2010/x32/Debug/lib.command.1.tlog new file mode 100644 index 0000000000000000000000000000000000000000..3bbbc74011c599ca107a90e4c2e38d7aa8ef8172 GIT binary patch literal 252 zcmZ|JK??yv7{>AcS@{l|yIIR^kLect)?KJFzk zA(o7sgdqiEQrZLyIa4A|q8LD({aHvDN%bvV4n%lahnm<_Xr7C$Rtk literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/scripts/vs2010/x32/Debug/pugixml.lastbuildstate b/LifeGraphics/pugixml/scripts/vs2010/x32/Debug/pugixml.lastbuildstate new file mode 100644 index 0000000..3f56bd7 --- /dev/null +++ b/LifeGraphics/pugixml/scripts/vs2010/x32/Debug/pugixml.lastbuildstate @@ -0,0 +1,2 @@ +#v4.0:v100 +Debug|Win32|E:\pugixml-1.0\scripts\| diff --git a/LifeGraphics/pugixml/scripts/vs2010/x32/Debug/pugixml.obj b/LifeGraphics/pugixml/scripts/vs2010/x32/Debug/pugixml.obj new file mode 100644 index 0000000000000000000000000000000000000000..1065b2f0b66604b72f17a87da6137a52854731f3 GIT binary patch literal 1150258 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∈XA6%!GaY<;};ILU(cjrI^;v+r*UD`Ois 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�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 z%0A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 YU8uMD%lN%B$5Zk7WHJBmY~A_me=%sLlK=n! literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/scripts/vs2010/x32/Debug/pugixml_vs2010_static.log b/LifeGraphics/pugixml/scripts/vs2010/x32/Debug/pugixml_vs2010_static.log new file mode 100644 index 0000000..3552f2d --- /dev/null +++ b/LifeGraphics/pugixml/scripts/vs2010/x32/Debug/pugixml_vs2010_static.log @@ -0,0 +1,18 @@ +Build started 29.05.2011 19:27:58. + 1>Project "E:\pugixml-1.0\scripts\pugixml_vs2010_static.vcxproj" on node 2 (rebuild target(s)). + 1>InitializeBuildStatus: + Creating "vs2010\x32\Debug\pugixml.unsuccessfulbuild" because "AlwaysCreate" was specified. + ClCompile: + C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\CL.exe /c /Zi /nologo /W3 /WX- /Od /Oy- /D _DEBUG /D _MBCS /Gm- /EHsc /RTC1 /MTd /GS /Gy /fp:precise /Zc:wchar_t /Zc:forScope /Fo"vs2010\x32\Debug\\" /Fd"vs2010\x32\pugixml_sd.pdb" /Gd /TP /analyze- /errorReport:prompt ..\src\pugixml.cpp + pugixml.cpp + Lib: + C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\Lib.exe /OUT:"vs2010\x32\pugixml_sd.lib" /NOLOGO vs2010\x32\Debug\pugixml.obj + pugixml_vs2010_static.vcxproj -> E:\pugixml-1.0\scripts\vs2010\x32\pugixml_sd.lib + FinalizeBuildStatus: + Deleting file "vs2010\x32\Debug\pugixml.unsuccessfulbuild". + Touching "vs2010\x32\Debug\pugixml.lastbuildstate". + 1>Done Building Project "E:\pugixml-1.0\scripts\pugixml_vs2010_static.vcxproj" (rebuild target(s)). + +Build succeeded. + +Time Elapsed 00:00:01.95 diff --git a/LifeGraphics/pugixml/scripts/vs2010/x32/Release/CL.read.1.tlog b/LifeGraphics/pugixml/scripts/vs2010/x32/Release/CL.read.1.tlog new file mode 100644 index 0000000000000000000000000000000000000000..ac240f05cfe17627e36122249f62586c054a3bb5 GIT binary patch literal 9254 zcmd6t&2HN;5QO(!pzn|uXizt|-fUTRTv(DJ$*~*+&HKMZ+iyrK64Fxx)MFTdD2hMB z-QjXb=J&6kQO5Upi9;M?8@m{yihX>EMcl_%^1EoG`0M*Uc?W)<;}a>>KT)@ibu^?p z-nX>U#446Nck#@#=h;w3Z8o&3_nLCk`x-18N*<}x(|^sgoAq&^Rzr=JGF9ABc1MXa z^;eND(s#AKq5fSwk=N0hFx-*qNw3Lkcv~>KJi@0mz7_exru+wfFXZkizoXQe)P9EJ z{aCv=r`AhQJ;XQaw!AMvR>$Lq>i1ZzO(UE4{xMHIzIp23>KE9lPBz{$hN+e8@Birw zc_Le1jCkR1;JsuVBZns5T@%NUyu5}F;;9G28CHwWUGj+sgXUR(v4Rta=Gfk9hbCEJ z315XoQ9uzVN3ukfU@!3G0Xp0q}rUUIH5i=FM8Jg z`^1pBCcLH&S>g{xPp!gEo0Rnb1BM)oC{9F+M(9d-(VJ#^ItSYWDFiA7~#tt)d<^Q84-(8C?BO zYq%FdR#YAg^LVg=k**Vj0X}L@>TFIb!efnbZc6jJFcg>gn9m_ScE~ehe!}I!kz?^tZYId2J>C$t4C3SvNQa+CqC&)HP`s{fNE9Pk=vQ4BNpkZ4eW5SG@m8k77AzF82#eIkUf$$zYoQb?!UAL+nTsJ w|Br!JR@auFRIg{I=$29lbW7<$F!k?aMRz$z)+w&*4lJJ$`1hkiFrDH40sq$jbpQYW literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/scripts/vs2010/x32/Release/CL.write.1.tlog b/LifeGraphics/pugixml/scripts/vs2010/x32/Release/CL.write.1.tlog new file mode 100644 index 0000000000000000000000000000000000000000..6906840c583aa4d59ca3153c78c96839fb3c8878 GIT binary patch literal 276 zcmb`C%L;=)3`Ngc=%;k>v2$m25XIu4Ra^}9|9?Wgx)Td-N+6J%TA`GGQo{s#|H`1KqL2$dD%c0WjM_!&$IhJ#DxKAXiye6!umP}pygVyq*{u}N5 N>nx_xeT5kNKO3alCz${M literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/scripts/vs2010/x32/Release/Lib-link.read.1.tlog b/LifeGraphics/pugixml/scripts/vs2010/x32/Release/Lib-link.read.1.tlog new file mode 100644 index 0000000..46b134b --- /dev/null +++ b/LifeGraphics/pugixml/scripts/vs2010/x32/Release/Lib-link.read.1.tlog @@ -0,0 +1 @@ +ÿþ \ No newline at end of file diff --git a/LifeGraphics/pugixml/scripts/vs2010/x32/Release/Lib-link.write.1.tlog b/LifeGraphics/pugixml/scripts/vs2010/x32/Release/Lib-link.write.1.tlog new file mode 100644 index 0000000000000000000000000000000000000000..8594e38df9eeba63726e2f65e5fe97358492597f GIT binary patch literal 212 zcmb`ByAD855JXQc@hNURIvsK&gb=SNZutM7z|e_Sv)P^5owMh$VM%fH%#>8Dv@`@n y7((eI1%vdSNxfrHjpam-yOL;f+OWE^WOD5%&Crok9h(M=uP!v-6WaA9oq;6g6m6 zqgJzeby}%Zp*4H2gU;*+1^%Nhd^a#rWcKh2hgpSdRK$mNJ8(AI+fM~2rGKA`H~tpiD^xkp{2?y~y7 G@9_d3`ZwtS literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/scripts/vs2010/x32/Release/cl.command.1.tlog b/LifeGraphics/pugixml/scripts/vs2010/x32/Release/cl.command.1.tlog new file mode 100644 index 0000000000000000000000000000000000000000..cc90e417bee430445677c3c3d01a4f38c92d95ab GIT binary patch literal 552 zcmbu6Pfx-?5XFCI6TgGuZWX+7L})b;C4i9-VpE}LlLDKHO8xNa+hI*ij3?8~&hDFk zZ(ct??&;BH#(-NAE}78hies8YoC+T?}O;=nx#=~cMEESuMW{oUAAr5{O>Uv!89Om~Khv;{9ZSN&c8#*d- zWfscM4WH`O=1u1lnW8D%ANW%2E6ttDyHFA9-r!XoTGFOz`s}n_OZp6zQ_3>yKNHfh zZZV5LYaB09sgN2g)uyV9G;6^_s7-YDN{pqn_Ze$hNzJ<^?X}FsvNEoofwS8-?7LB4 KXIuUEAAbRncT?X0 literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/scripts/vs2010/x32/Release/lib.command.1.tlog b/LifeGraphics/pugixml/scripts/vs2010/x32/Release/lib.command.1.tlog new file mode 100644 index 0000000000000000000000000000000000000000..1a7ef7ce8fa7e06a8ec928075c617427aadc9b13 GIT binary patch literal 258 zcmZ{eO$z~06o#L*@*gI9mti+Mbem`}4Wn7yh`MP+lxTiE-X;r@#rb^C`*fa0p}f+-1YLJVE)7mUcXPg$yWtdx6F!dflpYjV1BNEERm_`}HX@_4p#d;RmxG@L~{)c?Ey!}2J> literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/scripts/vs2010/x32/Release/pugixml.lastbuildstate b/LifeGraphics/pugixml/scripts/vs2010/x32/Release/pugixml.lastbuildstate new file mode 100644 index 0000000..d003a15 --- /dev/null +++ b/LifeGraphics/pugixml/scripts/vs2010/x32/Release/pugixml.lastbuildstate @@ -0,0 +1,2 @@ +#v4.0:v100 +Release|Win32|E:\pugixml-1.0\scripts\| diff --git a/LifeGraphics/pugixml/scripts/vs2010/x32/Release/pugixml.obj b/LifeGraphics/pugixml/scripts/vs2010/x32/Release/pugixml.obj new file mode 100644 index 0000000000000000000000000000000000000000..1f4c65c4b4d4b38d6902453923824cd26c4c9ea3 GIT binary patch literal 875422 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 literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/lib/pugixml_s.pdb b/LifeGraphics/pugixml/lib/pugixml_s.pdb new file mode 100644 index 0000000000000000000000000000000000000000..93cf114820dfd62de67b8579e11eaecac8b009b9 GIT binary patch literal 266240 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 b1qc)%P=G)I0tE;ZAW(on0RsP*Bk=zL#Gi@b literal 0 HcmV?d00001 diff --git a/LifeGraphics/pugixml/lib/pugixml_sd.lib b/LifeGraphics/pugixml/lib/pugixml_sd.lib new file mode 100644 index 0000000000000000000000000000000000000000..14742adb28ebfcb0ee4a606c15413530aa59937c GIT binary patch literal 1318866 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�a?Of>Rpzniz9eO@gX*?OKZ`2fr7XcwJ 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