Repository: Is-Daouda/is-Engine Branch: 4.0.x Commit: 318bbfb6a711 Files: 1788 Total size: 181.0 MB Directory structure: gitextract_09tg11nt/ ├── .gradle/ │ ├── 5.1.1/ │ │ └── gc.properties │ ├── buildOutputCleanup/ │ │ └── cache.properties │ └── vcs-1/ │ └── gc.properties ├── .idea/ │ ├── .gitignore │ ├── caches/ │ │ └── build_file_checksums.ser │ ├── codeStyles/ │ │ └── Project.xml │ ├── compiler.xml │ ├── encodings.xml │ ├── gradle.xml │ ├── is-Engine.iml │ ├── jarRepositories.xml │ ├── misc.xml │ ├── modules/ │ │ ├── app/ │ │ │ └── is-Engine.app.iml │ │ └── is-Engine.iml │ ├── modules.xml │ ├── runConfigurations.xml │ └── vcs.xml ├── CMakeLists.txt ├── LICENSE.md ├── README.md ├── app/ │ ├── CMakeLists.txt │ ├── build.gradle │ └── src/ │ └── main/ │ ├── .vs/ │ │ └── vs-sfml/ │ │ ├── FileContentIndex/ │ │ │ ├── 27af3472-9fff-485a-80f6-a98dd9f34d58.vsidx │ │ │ ├── 4d722eb7-4652-467b-a888-40e24ffc613c.vsidx │ │ │ └── 9cbe5ebd-1110-46d2-a43c-740b2c898a99.vsidx │ │ └── v17/ │ │ ├── .suo │ │ ├── DocumentLayout.json │ │ └── ipch/ │ │ └── AutoPCH/ │ │ ├── ee33401603bb6f9/ │ │ │ └── HELLOSCENE.ipch │ │ └── f64c3c32af64e908/ │ │ └── BASICSFMLMAIN.ipch │ ├── .vscode/ │ │ ├── _keybindings.json │ │ ├── c_cpp_properties.json │ │ ├── launch.json │ │ ├── settings.json │ │ └── tasks.json │ ├── AndroidManifest.xml │ ├── CMakeLists.txt │ ├── Makefile │ ├── Makefile-vscode │ ├── SDL2 Template.aps │ ├── SDL2 Template.rc │ ├── SDL2_SFML.sln │ ├── SDL2_SFML.vcxproj │ ├── SDL2_SFML.vcxproj.filters │ ├── SDL2_SFML.vcxproj.user │ ├── build.sh │ ├── cmake/ │ │ ├── app_src.cmake │ │ ├── isengine.cmake │ │ └── resource.rc │ ├── codeblocks/ │ │ └── resource.rc │ ├── copy_assets.cmd │ ├── cpp/ │ │ ├── GameDialog.cpp │ │ ├── GameDisplay.cpp │ │ ├── GameEngine.cpp │ │ ├── GameFunction.cpp │ │ ├── GameKeyData.cpp │ │ ├── GameSlider.cpp │ │ ├── GameSystem.cpp │ │ ├── GameSystemExtended.cpp │ │ ├── GameTime.cpp │ │ ├── Main.hpp │ │ ├── MainObject.cpp │ │ ├── PCH.hpp │ │ ├── SDL_android_main.c │ │ ├── TransitionEffect.cpp │ │ ├── app_src/ │ │ │ ├── activity/ │ │ │ │ └── GameActivity.h │ │ │ ├── config/ │ │ │ │ ├── ExtraConfig.h │ │ │ │ └── GameConfig.h │ │ │ ├── gamesystem_ext/ │ │ │ │ └── GameSystemExtended.h │ │ │ ├── language/ │ │ │ │ └── GameLanguage.h │ │ │ ├── levels/ │ │ │ │ └── Level.h │ │ │ ├── objects/ │ │ │ │ ├── HelloWorld.h │ │ │ │ └── widgets/ │ │ │ │ └── GameDialog.h │ │ │ └── scenes/ │ │ │ └── HelloScene/ │ │ │ └── HelloScene.h │ │ ├── b2BlockAllocator.cpp │ │ ├── b2Body.cpp │ │ ├── b2BroadPhase.cpp │ │ ├── b2ChainAndCircleContact.cpp │ │ ├── b2ChainAndPolygonContact.cpp │ │ ├── b2ChainShape.cpp │ │ ├── b2CircleContact.cpp │ │ ├── b2CircleShape.cpp │ │ ├── b2CollideCircle.cpp │ │ ├── b2CollideEdge.cpp │ │ ├── b2CollidePolygon.cpp │ │ ├── b2Collision.cpp │ │ ├── b2Contact.cpp │ │ ├── b2ContactManager.cpp │ │ ├── b2ContactSolver.cpp │ │ ├── b2Distance.cpp │ │ ├── b2DistanceJoint.cpp │ │ ├── b2Draw.cpp │ │ ├── b2DynamicTree.cpp │ │ ├── b2EdgeAndCircleContact.cpp │ │ ├── b2EdgeAndPolygonContact.cpp │ │ ├── b2EdgeShape.cpp │ │ ├── b2Fixture.cpp │ │ ├── b2FrictionJoint.cpp │ │ ├── b2GearJoint.cpp │ │ ├── b2Island.cpp │ │ ├── b2Joint.cpp │ │ ├── b2Math.cpp │ │ ├── b2MotorJoint.cpp │ │ ├── b2MouseJoint.cpp │ │ ├── b2PolygonAndCircleContact.cpp │ │ ├── b2PolygonContact.cpp │ │ ├── b2PolygonShape.cpp │ │ ├── b2PrismaticJoint.cpp │ │ ├── b2PulleyJoint.cpp │ │ ├── b2RevoluteJoint.cpp │ │ ├── b2Rope.cpp │ │ ├── b2RopeJoint.cpp │ │ ├── b2Settings.cpp │ │ ├── b2StackAllocator.cpp │ │ ├── b2TimeOfImpact.cpp │ │ ├── b2Timer.cpp │ │ ├── b2WeldJoint.cpp │ │ ├── b2WheelJoint.cpp │ │ ├── b2World.cpp │ │ ├── b2WorldCallbacks.cpp │ │ ├── basicSFMLmain.cpp │ │ ├── isEngine/ │ │ │ ├── core/ │ │ │ │ ├── ActivityController.h │ │ │ │ └── GameEngine.h │ │ │ ├── ext_lib/ │ │ │ │ ├── Box2D/ │ │ │ │ │ ├── Box2D.h │ │ │ │ │ ├── Collision/ │ │ │ │ │ │ ├── Shapes/ │ │ │ │ │ │ │ ├── b2ChainShape.h │ │ │ │ │ │ │ ├── b2CircleShape.h │ │ │ │ │ │ │ ├── b2EdgeShape.h │ │ │ │ │ │ │ ├── b2PolygonShape.h │ │ │ │ │ │ │ └── b2Shape.h │ │ │ │ │ │ ├── b2BroadPhase.h │ │ │ │ │ │ ├── b2Collision.h │ │ │ │ │ │ ├── b2Distance.h │ │ │ │ │ │ ├── b2DynamicTree.h │ │ │ │ │ │ └── b2TimeOfImpact.h │ │ │ │ │ ├── Common/ │ │ │ │ │ │ ├── b2BlockAllocator.h │ │ │ │ │ │ ├── b2Draw.h │ │ │ │ │ │ ├── b2GrowableStack.h │ │ │ │ │ │ ├── b2Math.h │ │ │ │ │ │ ├── b2Settings.h │ │ │ │ │ │ ├── b2StackAllocator.h │ │ │ │ │ │ └── b2Timer.h │ │ │ │ │ ├── Dynamics/ │ │ │ │ │ │ ├── Contacts/ │ │ │ │ │ │ │ ├── b2ChainAndCircleContact.h │ │ │ │ │ │ │ ├── b2ChainAndPolygonContact.h │ │ │ │ │ │ │ ├── b2CircleContact.h │ │ │ │ │ │ │ ├── b2Contact.h │ │ │ │ │ │ │ ├── b2ContactSolver.h │ │ │ │ │ │ │ ├── b2EdgeAndCircleContact.h │ │ │ │ │ │ │ ├── b2EdgeAndPolygonContact.h │ │ │ │ │ │ │ ├── b2PolygonAndCircleContact.h │ │ │ │ │ │ │ └── b2PolygonContact.h │ │ │ │ │ │ ├── Joints/ │ │ │ │ │ │ │ ├── b2DistanceJoint.h │ │ │ │ │ │ │ ├── b2FrictionJoint.h │ │ │ │ │ │ │ ├── b2GearJoint.h │ │ │ │ │ │ │ ├── b2Joint.h │ │ │ │ │ │ │ ├── b2MotorJoint.h │ │ │ │ │ │ │ ├── b2MouseJoint.h │ │ │ │ │ │ │ ├── b2PrismaticJoint.h │ │ │ │ │ │ │ ├── b2PulleyJoint.h │ │ │ │ │ │ │ ├── b2RevoluteJoint.h │ │ │ │ │ │ │ ├── b2RopeJoint.h │ │ │ │ │ │ │ ├── b2WeldJoint.h │ │ │ │ │ │ │ └── b2WheelJoint.h │ │ │ │ │ │ ├── b2Body.h │ │ │ │ │ │ ├── b2ContactManager.h │ │ │ │ │ │ ├── b2Fixture.h │ │ │ │ │ │ ├── b2Island.h │ │ │ │ │ │ ├── b2TimeStep.h │ │ │ │ │ │ ├── b2World.h │ │ │ │ │ │ └── b2WorldCallbacks.h │ │ │ │ │ └── Rope/ │ │ │ │ │ └── b2Rope.h │ │ │ │ ├── TMXLite/ │ │ │ │ │ ├── Config.hpp │ │ │ │ │ ├── FreeFuncs.cpp │ │ │ │ │ ├── FreeFuncs.hpp │ │ │ │ │ ├── ImageLayer.cpp │ │ │ │ │ ├── ImageLayer.hpp │ │ │ │ │ ├── Layer.hpp │ │ │ │ │ ├── LayerGroup.cpp │ │ │ │ │ ├── LayerGroup.hpp │ │ │ │ │ ├── Map.cpp │ │ │ │ │ ├── Map.hpp │ │ │ │ │ ├── Object.cpp │ │ │ │ │ ├── Object.hpp │ │ │ │ │ ├── ObjectGroup.cpp │ │ │ │ │ ├── ObjectGroup.hpp │ │ │ │ │ ├── Property.cpp │ │ │ │ │ ├── Property.hpp │ │ │ │ │ ├── TileLayer.cpp │ │ │ │ │ ├── TileLayer.hpp │ │ │ │ │ ├── Tileset.cpp │ │ │ │ │ ├── Tileset.hpp │ │ │ │ │ ├── Types.hpp │ │ │ │ │ ├── Types.inl │ │ │ │ │ ├── detail/ │ │ │ │ │ │ ├── Android.hpp │ │ │ │ │ │ ├── Log.hpp │ │ │ │ │ │ ├── pugiconfig.hpp │ │ │ │ │ │ ├── pugixml.LICENSE │ │ │ │ │ │ ├── pugixml.cpp │ │ │ │ │ │ └── pugixml.hpp │ │ │ │ │ ├── miniz.c │ │ │ │ │ └── miniz.h │ │ │ │ └── TinyFileDialogs/ │ │ │ │ ├── TinyDialogBox.h │ │ │ │ ├── tinyfiledialogs.cpp │ │ │ │ └── tinyfiledialogs.h │ │ │ └── system/ │ │ │ ├── display/ │ │ │ │ ├── GameDisplay.h │ │ │ │ ├── SDM.h │ │ │ │ └── SDMBlitSDLSprite.h │ │ │ ├── entity/ │ │ │ │ ├── Background.h │ │ │ │ ├── Button.h │ │ │ │ ├── Form.h │ │ │ │ ├── MainObject.h │ │ │ │ └── parents/ │ │ │ │ ├── DepthObject.h │ │ │ │ ├── Destructible.h │ │ │ │ ├── FilePath.h │ │ │ │ ├── Health.h │ │ │ │ ├── HurtEffect.h │ │ │ │ ├── Name.h │ │ │ │ ├── ScorePoint.h │ │ │ │ ├── Step.h │ │ │ │ ├── Type.h │ │ │ │ └── Visibilty.h │ │ │ ├── function/ │ │ │ │ ├── GameFunction.h │ │ │ │ ├── GameKeyData.h │ │ │ │ ├── GameKeyName.h │ │ │ │ ├── GameSlider.h │ │ │ │ ├── GameSystem.h │ │ │ │ └── GameTime.h │ │ │ ├── graphic/ │ │ │ │ ├── GRM.h │ │ │ │ ├── GameFont.h │ │ │ │ ├── GameTexture.h │ │ │ │ └── TransitionEffect.h │ │ │ ├── islibconnect/ │ │ │ │ ├── isEngineSDLWrapper.h │ │ │ │ ├── isEngineVector2Wrapper.inl │ │ │ │ ├── isEngineWrapper.h │ │ │ │ └── isLibConnect.h │ │ │ └── sound/ │ │ │ ├── GSM.h │ │ │ ├── GameMusic.h │ │ │ └── GameSound.h │ │ ├── isEngineSDLWrapper.cpp │ │ ├── isEngineWrapper.cpp │ │ ├── main.cpp │ │ ├── resource.h │ │ └── vscode/ │ │ ├── Linux/ │ │ │ ├── LinuxHelper.cpp │ │ │ └── LinuxHelper.hpp │ │ ├── MacOS/ │ │ │ ├── MacHelper.cpp │ │ │ └── MacHelper.hpp │ │ ├── Utility/ │ │ │ └── Types.hpp │ │ └── Win32/ │ │ ├── Icon.h │ │ ├── Icon.rc │ │ ├── WindowsHelper.cpp │ │ └── WindowsHelper.hpp │ ├── env/ │ │ ├── .all.mk │ │ ├── .debug.mk │ │ ├── .release.mk │ │ ├── linux/ │ │ │ └── exec.desktop │ │ ├── linux.all.mk │ │ ├── osx/ │ │ │ ├── Info.plist.json │ │ │ └── dmg.applescript │ │ ├── osx.all.mk │ │ ├── osx.debug.mk │ │ ├── rpi.release.mk │ │ ├── windows/ │ │ │ ├── win-make_icon_from256.sh │ │ │ └── win-make_icon_from_all.sh │ │ ├── windows.all.mk │ │ ├── windows.debug.mk │ │ └── windows.release.mk │ ├── external/ │ │ └── SFML/ │ │ ├── include/ │ │ │ └── SFML/ │ │ │ ├── Audio/ │ │ │ │ ├── AlResource.hpp │ │ │ │ ├── Export.hpp │ │ │ │ ├── InputSoundFile.hpp │ │ │ │ ├── Listener.hpp │ │ │ │ ├── Music.hpp │ │ │ │ ├── OutputSoundFile.hpp │ │ │ │ ├── Sound.hpp │ │ │ │ ├── SoundBuffer.hpp │ │ │ │ ├── SoundBufferRecorder.hpp │ │ │ │ ├── SoundFileFactory.hpp │ │ │ │ ├── SoundFileFactory.inl │ │ │ │ ├── SoundFileReader.hpp │ │ │ │ ├── SoundFileWriter.hpp │ │ │ │ ├── SoundRecorder.hpp │ │ │ │ ├── SoundSource.hpp │ │ │ │ └── SoundStream.hpp │ │ │ ├── Audio.hpp │ │ │ ├── Config.hpp │ │ │ ├── GpuPreference.hpp │ │ │ ├── Graphics/ │ │ │ │ ├── BlendMode.hpp │ │ │ │ ├── CircleShape.hpp │ │ │ │ ├── Color.hpp │ │ │ │ ├── ConvexShape.hpp │ │ │ │ ├── Drawable.hpp │ │ │ │ ├── Export.hpp │ │ │ │ ├── Font.hpp │ │ │ │ ├── Glsl.hpp │ │ │ │ ├── Glsl.inl │ │ │ │ ├── Glyph.hpp │ │ │ │ ├── Image.hpp │ │ │ │ ├── PrimitiveType.hpp │ │ │ │ ├── Rect.hpp │ │ │ │ ├── Rect.inl │ │ │ │ ├── RectangleShape.hpp │ │ │ │ ├── RenderStates.hpp │ │ │ │ ├── RenderTarget.hpp │ │ │ │ ├── RenderTexture.hpp │ │ │ │ ├── RenderWindow.hpp │ │ │ │ ├── Shader.hpp │ │ │ │ ├── Shape.hpp │ │ │ │ ├── Sprite.hpp │ │ │ │ ├── Text.hpp │ │ │ │ ├── Texture.hpp │ │ │ │ ├── Transform.hpp │ │ │ │ ├── Transformable.hpp │ │ │ │ ├── Vertex.hpp │ │ │ │ ├── VertexArray.hpp │ │ │ │ ├── VertexBuffer.hpp │ │ │ │ └── View.hpp │ │ │ ├── Graphics.hpp │ │ │ ├── Main.hpp │ │ │ ├── Network/ │ │ │ │ ├── Export.hpp │ │ │ │ ├── Ftp.hpp │ │ │ │ ├── Http.hpp │ │ │ │ ├── IpAddress.hpp │ │ │ │ ├── Packet.hpp │ │ │ │ ├── Socket.hpp │ │ │ │ ├── SocketHandle.hpp │ │ │ │ ├── SocketSelector.hpp │ │ │ │ ├── TcpListener.hpp │ │ │ │ ├── TcpSocket.hpp │ │ │ │ └── UdpSocket.hpp │ │ │ ├── Network.hpp │ │ │ ├── OpenGL.hpp │ │ │ ├── System/ │ │ │ │ ├── Clock.hpp │ │ │ │ ├── Err.hpp │ │ │ │ ├── Export.hpp │ │ │ │ ├── FileInputStream.hpp │ │ │ │ ├── InputStream.hpp │ │ │ │ ├── Lock.hpp │ │ │ │ ├── MemoryInputStream.hpp │ │ │ │ ├── Mutex.hpp │ │ │ │ ├── NativeActivity.hpp │ │ │ │ ├── NonCopyable.hpp │ │ │ │ ├── Sleep.hpp │ │ │ │ ├── String.hpp │ │ │ │ ├── String.inl │ │ │ │ ├── Thread.hpp │ │ │ │ ├── Thread.inl │ │ │ │ ├── ThreadLocal.hpp │ │ │ │ ├── ThreadLocalPtr.hpp │ │ │ │ ├── ThreadLocalPtr.inl │ │ │ │ ├── Time.hpp │ │ │ │ ├── Utf.hpp │ │ │ │ ├── Utf.inl │ │ │ │ ├── Vector2.hpp │ │ │ │ ├── Vector2.inl │ │ │ │ ├── Vector3.hpp │ │ │ │ └── Vector3.inl │ │ │ ├── System.hpp │ │ │ ├── Window/ │ │ │ │ ├── Clipboard.hpp │ │ │ │ ├── Context.hpp │ │ │ │ ├── ContextSettings.hpp │ │ │ │ ├── Cursor.hpp │ │ │ │ ├── Event.hpp │ │ │ │ ├── Export.hpp │ │ │ │ ├── GlResource.hpp │ │ │ │ ├── Joystick.hpp │ │ │ │ ├── Keyboard.hpp │ │ │ │ ├── Mouse.hpp │ │ │ │ ├── Sensor.hpp │ │ │ │ ├── Touch.hpp │ │ │ │ ├── VideoMode.hpp │ │ │ │ ├── Vulkan.hpp │ │ │ │ ├── Window.hpp │ │ │ │ ├── WindowBase.hpp │ │ │ │ ├── WindowHandle.hpp │ │ │ │ └── WindowStyle.hpp │ │ │ └── Window.hpp │ │ └── lib/ │ │ ├── Debug/ │ │ │ ├── sfml-audio-s-d.pdb │ │ │ ├── sfml-audio-s.pdb │ │ │ ├── sfml-audio.pdb │ │ │ ├── sfml-graphics-s-d.pdb │ │ │ ├── sfml-graphics-s.pdb │ │ │ ├── sfml-graphics.pdb │ │ │ ├── sfml-main-d.pdb │ │ │ ├── sfml-main-s.pdb │ │ │ ├── sfml-network-s-d.pdb │ │ │ ├── sfml-network-s.pdb │ │ │ ├── sfml-network.pdb │ │ │ ├── sfml-system-s-d.pdb │ │ │ ├── sfml-system-s.pdb │ │ │ ├── sfml-system.pdb │ │ │ ├── sfml-window-s-d.pdb │ │ │ ├── sfml-window-s.pdb │ │ │ └── sfml-window.pdb │ │ ├── cmake/ │ │ │ └── SFML/ │ │ │ ├── SFMLConfig.cmake │ │ │ ├── SFMLConfigDependencies.cmake │ │ │ ├── SFMLConfigVersion.cmake │ │ │ ├── SFMLSharedTargets-debug.cmake │ │ │ ├── SFMLSharedTargets-release.cmake │ │ │ ├── SFMLSharedTargets.cmake │ │ │ ├── SFMLStaticTargets-debug.cmake │ │ │ ├── SFMLStaticTargets-release.cmake │ │ │ └── SFMLStaticTargets.cmake │ │ ├── flac.lib │ │ ├── freetype.lib │ │ ├── ogg.lib │ │ ├── openal32.lib │ │ ├── sfml-audio-d.lib │ │ ├── sfml-audio-s-d.lib │ │ ├── sfml-audio-s.lib │ │ ├── sfml-audio.lib │ │ ├── sfml-graphics-d.lib │ │ ├── sfml-graphics-s-d.lib │ │ ├── sfml-graphics-s.lib │ │ ├── sfml-graphics.lib │ │ ├── sfml-main-d.lib │ │ ├── sfml-main.lib │ │ ├── sfml-network-d.lib │ │ ├── sfml-network-s-d.lib │ │ ├── sfml-network-s.lib │ │ ├── sfml-network.lib │ │ ├── sfml-system-d.lib │ │ ├── sfml-system-s-d.lib │ │ ├── sfml-system-s.lib │ │ ├── sfml-system.lib │ │ ├── sfml-window-d.lib │ │ ├── sfml-window-s-d.lib │ │ ├── sfml-window-s.lib │ │ ├── sfml-window.lib │ │ ├── vorbis.lib │ │ ├── vorbisenc.lib │ │ └── vorbisfile.lib │ ├── is-Engine-linux-SDL2.cbp │ ├── is-Engine-linux.cbp │ ├── is-Engine-windows-SDL2.cbp │ ├── is-Engine-windows.cbp │ ├── java/ │ │ └── com/ │ │ └── author/ │ │ └── isengine/ │ │ └── SDLActivity.java │ ├── lib/ │ │ └── catch2/ │ │ └── catch.hpp │ ├── qt/ │ │ ├── is-Engine.pro │ │ └── is-Engine.pro.user │ ├── res/ │ │ └── values/ │ │ └── strings.xml │ ├── vs-sfml.sln │ ├── vs-sfml.vcxproj │ ├── vs-sfml.vcxproj.filters │ ├── vs-sfml.vcxproj.user │ └── web/ │ ├── index.html │ ├── scripts/ │ │ └── main.js │ ├── styles/ │ │ └── index.css │ └── sw.js ├── build.gradle ├── doc/ │ ├── isEngine_api_doc_eng.html │ └── isEngine_api_doc_fr.html ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── libs/ │ ├── SDL2/ │ │ ├── CMakeFiles/ │ │ │ └── SDL2.dir/ │ │ │ └── src/ │ │ │ ├── SDL.c.o │ │ │ ├── SDL_assert.c.o │ │ │ ├── SDL_error.c.o │ │ │ ├── SDL_hints.c.o │ │ │ ├── SDL_log.c.o │ │ │ ├── atomic/ │ │ │ │ ├── SDL_atomic.c.o │ │ │ │ └── SDL_spinlock.c.o │ │ │ ├── audio/ │ │ │ │ ├── SDL_audio.c.o │ │ │ │ ├── SDL_audiocvt.c.o │ │ │ │ ├── SDL_audiodev.c.o │ │ │ │ ├── SDL_audiotypecvt.c.o │ │ │ │ ├── SDL_mixer.c.o │ │ │ │ ├── SDL_wave.c.o │ │ │ │ ├── android/ │ │ │ │ │ └── SDL_androidaudio.c.o │ │ │ │ ├── dummy/ │ │ │ │ │ └── SDL_dummyaudio.c.o │ │ │ │ └── paudio/ │ │ │ │ └── SDL_paudio.c.o │ │ │ ├── core/ │ │ │ │ └── android/ │ │ │ │ └── SDL_android.c.o │ │ │ ├── cpuinfo/ │ │ │ │ └── SDL_cpuinfo.c.o │ │ │ ├── dynapi/ │ │ │ │ └── SDL_dynapi.c.o │ │ │ ├── events/ │ │ │ │ ├── SDL_clipboardevents.c.o │ │ │ │ ├── SDL_dropevents.c.o │ │ │ │ ├── SDL_events.c.o │ │ │ │ ├── SDL_gesture.c.o │ │ │ │ ├── SDL_keyboard.c.o │ │ │ │ ├── SDL_mouse.c.o │ │ │ │ ├── SDL_quit.c.o │ │ │ │ ├── SDL_touch.c.o │ │ │ │ └── SDL_windowevents.c.o │ │ │ ├── file/ │ │ │ │ └── SDL_rwops.c.o │ │ │ ├── filesystem/ │ │ │ │ └── android/ │ │ │ │ └── SDL_sysfilesystem.c.o │ │ │ ├── haptic/ │ │ │ │ ├── SDL_haptic.c.o │ │ │ │ └── dummy/ │ │ │ │ └── SDL_syshaptic.c.o │ │ │ ├── joystick/ │ │ │ │ ├── SDL_gamecontroller.c.o │ │ │ │ ├── SDL_joystick.c.o │ │ │ │ └── android/ │ │ │ │ └── SDL_sysjoystick.c.o │ │ │ ├── libm/ │ │ │ │ ├── e_atan2.c.o │ │ │ │ ├── e_log.c.o │ │ │ │ ├── e_pow.c.o │ │ │ │ ├── e_rem_pio2.c.o │ │ │ │ ├── e_sqrt.c.o │ │ │ │ ├── k_cos.c.o │ │ │ │ ├── k_rem_pio2.c.o │ │ │ │ ├── k_sin.c.o │ │ │ │ ├── k_tan.c.o │ │ │ │ ├── s_atan.c.o │ │ │ │ ├── s_copysign.c.o │ │ │ │ ├── s_cos.c.o │ │ │ │ ├── s_fabs.c.o │ │ │ │ ├── s_floor.c.o │ │ │ │ ├── s_scalbn.c.o │ │ │ │ ├── s_sin.c.o │ │ │ │ └── s_tan.c.o │ │ │ ├── loadso/ │ │ │ │ └── dlopen/ │ │ │ │ └── SDL_sysloadso.c.o │ │ │ ├── power/ │ │ │ │ ├── SDL_power.c.o │ │ │ │ └── android/ │ │ │ │ └── SDL_syspower.c.o │ │ │ ├── render/ │ │ │ │ ├── SDL_d3dmath.c.o │ │ │ │ ├── SDL_render.c.o │ │ │ │ ├── SDL_yuv_mmx.c.o │ │ │ │ ├── SDL_yuv_sw.c.o │ │ │ │ ├── opengl/ │ │ │ │ │ ├── SDL_render_gl.c.o │ │ │ │ │ └── SDL_shaders_gl.c.o │ │ │ │ ├── opengles/ │ │ │ │ │ └── SDL_render_gles.c.o │ │ │ │ ├── opengles2/ │ │ │ │ │ ├── SDL_render_gles2.c.o │ │ │ │ │ └── SDL_shaders_gles2.c.o │ │ │ │ ├── psp/ │ │ │ │ │ └── SDL_render_psp.c.o │ │ │ │ └── software/ │ │ │ │ ├── SDL_blendfillrect.c.o │ │ │ │ ├── SDL_blendline.c.o │ │ │ │ ├── SDL_blendpoint.c.o │ │ │ │ ├── SDL_drawline.c.o │ │ │ │ ├── SDL_drawpoint.c.o │ │ │ │ ├── SDL_render_sw.c.o │ │ │ │ └── SDL_rotate.c.o │ │ │ ├── stdlib/ │ │ │ │ ├── SDL_getenv.c.o │ │ │ │ ├── SDL_iconv.c.o │ │ │ │ ├── SDL_malloc.c.o │ │ │ │ ├── SDL_qsort.c.o │ │ │ │ ├── SDL_stdlib.c.o │ │ │ │ └── SDL_string.c.o │ │ │ ├── test/ │ │ │ │ ├── SDL_test_assert.c.o │ │ │ │ ├── SDL_test_common.c.o │ │ │ │ ├── SDL_test_compare.c.o │ │ │ │ ├── SDL_test_crc32.c.o │ │ │ │ ├── SDL_test_font.c.o │ │ │ │ ├── SDL_test_fuzzer.c.o │ │ │ │ ├── SDL_test_harness.c.o │ │ │ │ ├── SDL_test_imageBlit.c.o │ │ │ │ ├── SDL_test_imageBlitBlend.c.o │ │ │ │ ├── SDL_test_imageFace.c.o │ │ │ │ ├── SDL_test_imagePrimitives.c.o │ │ │ │ ├── SDL_test_imagePrimitivesBlend.c.o │ │ │ │ ├── SDL_test_log.c.o │ │ │ │ ├── SDL_test_md5.c.o │ │ │ │ └── SDL_test_random.c.o │ │ │ ├── thread/ │ │ │ │ ├── SDL_thread.c.o │ │ │ │ └── pthread/ │ │ │ │ ├── SDL_syscond.c.o │ │ │ │ ├── SDL_sysmutex.c.o │ │ │ │ ├── SDL_syssem.c.o │ │ │ │ ├── SDL_systhread.c.o │ │ │ │ └── SDL_systls.c.o │ │ │ ├── timer/ │ │ │ │ ├── SDL_timer.c.o │ │ │ │ └── unix/ │ │ │ │ └── SDL_systimer.c.o │ │ │ └── video/ │ │ │ ├── SDL_RLEaccel.c.o │ │ │ ├── SDL_blit.c.o │ │ │ ├── SDL_blit_0.c.o │ │ │ ├── SDL_blit_1.c.o │ │ │ ├── SDL_blit_A.c.o │ │ │ ├── SDL_blit_N.c.o │ │ │ ├── SDL_blit_auto.c.o │ │ │ ├── SDL_blit_copy.c.o │ │ │ ├── SDL_blit_slow.c.o │ │ │ ├── SDL_bmp.c.o │ │ │ ├── SDL_clipboard.c.o │ │ │ ├── SDL_egl.c.o │ │ │ ├── SDL_fillrect.c.o │ │ │ ├── SDL_pixels.c.o │ │ │ ├── SDL_rect.c.o │ │ │ ├── SDL_shape.c.o │ │ │ ├── SDL_stretch.c.o │ │ │ ├── SDL_surface.c.o │ │ │ ├── SDL_video.c.o │ │ │ └── android/ │ │ │ ├── SDL_androidclipboard.c.o │ │ │ ├── SDL_androidevents.c.o │ │ │ ├── SDL_androidgl.c.o │ │ │ ├── SDL_androidkeyboard.c.o │ │ │ ├── SDL_androidmessagebox.c.o │ │ │ ├── SDL_androidmouse.c.o │ │ │ ├── SDL_androidtouch.c.o │ │ │ ├── SDL_androidvideo.c.o │ │ │ └── SDL_androidwindow.c.o │ │ ├── CMakeLists.txt │ │ ├── build.gradle │ │ ├── cmake_install.cmake │ │ ├── include/ │ │ │ ├── SDL.h │ │ │ ├── SDL_assert.h │ │ │ ├── SDL_atomic.h │ │ │ ├── SDL_audio.h │ │ │ ├── SDL_bits.h │ │ │ ├── SDL_blendmode.h │ │ │ ├── SDL_clipboard.h │ │ │ ├── SDL_config.h │ │ │ ├── SDL_config.h.cmake │ │ │ ├── SDL_config.h.in │ │ │ ├── SDL_config_android.h │ │ │ ├── SDL_config_iphoneos.h │ │ │ ├── SDL_config_macosx.h │ │ │ ├── SDL_config_minimal.h │ │ │ ├── SDL_config_pandora.h │ │ │ ├── SDL_config_psp.h │ │ │ ├── SDL_config_windows.h │ │ │ ├── SDL_config_winrt.h │ │ │ ├── SDL_config_wiz.h │ │ │ ├── SDL_copying.h │ │ │ ├── SDL_cpuinfo.h │ │ │ ├── SDL_egl.h │ │ │ ├── SDL_endian.h │ │ │ ├── SDL_error.h │ │ │ ├── SDL_events.h │ │ │ ├── SDL_filesystem.h │ │ │ ├── SDL_gamecontroller.h │ │ │ ├── SDL_gesture.h │ │ │ ├── SDL_haptic.h │ │ │ ├── SDL_hints.h │ │ │ ├── SDL_joystick.h │ │ │ ├── SDL_keyboard.h │ │ │ ├── SDL_keycode.h │ │ │ ├── SDL_loadso.h │ │ │ ├── SDL_log.h │ │ │ ├── SDL_main.h │ │ │ ├── SDL_messagebox.h │ │ │ ├── SDL_mouse.h │ │ │ ├── SDL_mutex.h │ │ │ ├── SDL_name.h │ │ │ ├── SDL_opengl.h │ │ │ ├── SDL_opengl_glext.h │ │ │ ├── SDL_opengles.h │ │ │ ├── SDL_opengles2.h │ │ │ ├── SDL_opengles2_gl2.h │ │ │ ├── SDL_opengles2_gl2ext.h │ │ │ ├── SDL_opengles2_gl2platform.h │ │ │ ├── SDL_opengles2_khrplatform.h │ │ │ ├── SDL_pixels.h │ │ │ ├── SDL_platform.h │ │ │ ├── SDL_power.h │ │ │ ├── SDL_quit.h │ │ │ ├── SDL_rect.h │ │ │ ├── SDL_render.h │ │ │ ├── SDL_revision.h │ │ │ ├── SDL_rwops.h │ │ │ ├── SDL_scancode.h │ │ │ ├── SDL_shape.h │ │ │ ├── SDL_stdinc.h │ │ │ ├── SDL_surface.h │ │ │ ├── SDL_system.h │ │ │ ├── SDL_syswm.h │ │ │ ├── SDL_test.h │ │ │ ├── SDL_test_assert.h │ │ │ ├── SDL_test_common.h │ │ │ ├── SDL_test_compare.h │ │ │ ├── SDL_test_crc32.h │ │ │ ├── SDL_test_font.h │ │ │ ├── SDL_test_fuzzer.h │ │ │ ├── SDL_test_harness.h │ │ │ ├── SDL_test_images.h │ │ │ ├── SDL_test_log.h │ │ │ ├── SDL_test_md5.h │ │ │ ├── SDL_test_random.h │ │ │ ├── SDL_thread.h │ │ │ ├── SDL_timer.h │ │ │ ├── SDL_touch.h │ │ │ ├── SDL_types.h │ │ │ ├── SDL_version.h │ │ │ ├── SDL_video.h │ │ │ ├── begin_code.h │ │ │ └── close_code.h │ │ └── src/ │ │ ├── SDL.c │ │ ├── SDL_assert.c │ │ ├── SDL_assert_c.h │ │ ├── SDL_error.c │ │ ├── SDL_error_c.h │ │ ├── SDL_hints.c │ │ ├── SDL_internal.h │ │ ├── SDL_log.c │ │ ├── atomic/ │ │ │ ├── SDL_atomic.c │ │ │ └── SDL_spinlock.c │ │ ├── audio/ │ │ │ ├── SDL_audio.c │ │ │ ├── SDL_audio_c.h │ │ │ ├── SDL_audiocvt.c │ │ │ ├── SDL_audiodev.c │ │ │ ├── SDL_audiodev_c.h │ │ │ ├── SDL_audiotypecvt.c │ │ │ ├── SDL_mixer.c │ │ │ ├── SDL_sysaudio.h │ │ │ ├── SDL_wave.c │ │ │ ├── SDL_wave.h │ │ │ ├── android/ │ │ │ │ ├── SDL_androidaudio.c │ │ │ │ └── SDL_androidaudio.h │ │ │ ├── dummy/ │ │ │ │ ├── SDL_dummyaudio.c │ │ │ │ └── SDL_dummyaudio.h │ │ │ ├── paudio/ │ │ │ │ ├── SDL_paudio.c │ │ │ │ └── SDL_paudio.h │ │ │ └── sdlgenaudiocvt.pl │ │ ├── core/ │ │ │ └── android/ │ │ │ ├── SDL_android.c │ │ │ └── SDL_android.h │ │ ├── cpuinfo/ │ │ │ └── SDL_cpuinfo.c │ │ ├── dynapi/ │ │ │ ├── SDL_dynapi.c │ │ │ ├── SDL_dynapi.h │ │ │ ├── SDL_dynapi_overrides.h │ │ │ ├── SDL_dynapi_procs.h │ │ │ └── gendynapi.pl │ │ ├── events/ │ │ │ ├── SDL_clipboardevents.c │ │ │ ├── SDL_clipboardevents_c.h │ │ │ ├── SDL_dropevents.c │ │ │ ├── SDL_dropevents_c.h │ │ │ ├── SDL_events.c │ │ │ ├── SDL_events_c.h │ │ │ ├── SDL_gesture.c │ │ │ ├── SDL_gesture_c.h │ │ │ ├── SDL_keyboard.c │ │ │ ├── SDL_keyboard_c.h │ │ │ ├── SDL_mouse.c │ │ │ ├── SDL_mouse_c.h │ │ │ ├── SDL_quit.c │ │ │ ├── SDL_sysevents.h │ │ │ ├── SDL_touch.c │ │ │ ├── SDL_touch_c.h │ │ │ ├── SDL_windowevents.c │ │ │ ├── SDL_windowevents_c.h │ │ │ ├── blank_cursor.h │ │ │ ├── default_cursor.h │ │ │ ├── scancodes_darwin.h │ │ │ ├── scancodes_linux.h │ │ │ ├── scancodes_windows.h │ │ │ └── scancodes_xfree86.h │ │ ├── file/ │ │ │ └── SDL_rwops.c │ │ ├── filesystem/ │ │ │ └── android/ │ │ │ └── SDL_sysfilesystem.c │ │ ├── haptic/ │ │ │ ├── SDL_haptic.c │ │ │ ├── SDL_haptic_c.h │ │ │ ├── SDL_syshaptic.h │ │ │ └── dummy/ │ │ │ └── SDL_syshaptic.c │ │ ├── joystick/ │ │ │ ├── SDL_gamecontroller.c │ │ │ ├── SDL_gamecontrollerdb.h │ │ │ ├── SDL_joystick.c │ │ │ ├── SDL_joystick_c.h │ │ │ ├── SDL_sysjoystick.h │ │ │ ├── android/ │ │ │ │ ├── SDL_sysjoystick.c │ │ │ │ └── SDL_sysjoystick_c.h │ │ │ └── sort_controllers.py │ │ ├── libm/ │ │ │ ├── e_atan2.c │ │ │ ├── e_log.c │ │ │ ├── e_pow.c │ │ │ ├── e_rem_pio2.c │ │ │ ├── e_sqrt.c │ │ │ ├── k_cos.c │ │ │ ├── k_rem_pio2.c │ │ │ ├── k_sin.c │ │ │ ├── k_tan.c │ │ │ ├── math_libm.h │ │ │ ├── math_private.h │ │ │ ├── s_atan.c │ │ │ ├── s_copysign.c │ │ │ ├── s_cos.c │ │ │ ├── s_fabs.c │ │ │ ├── s_floor.c │ │ │ ├── s_scalbn.c │ │ │ ├── s_sin.c │ │ │ └── s_tan.c │ │ ├── loadso/ │ │ │ └── dlopen/ │ │ │ └── SDL_sysloadso.c │ │ ├── power/ │ │ │ ├── SDL_power.c │ │ │ └── android/ │ │ │ └── SDL_syspower.c │ │ ├── render/ │ │ │ ├── SDL_d3dmath.c │ │ │ ├── SDL_d3dmath.h │ │ │ ├── SDL_render.c │ │ │ ├── SDL_sysrender.h │ │ │ ├── SDL_yuv_mmx.c │ │ │ ├── SDL_yuv_sw.c │ │ │ ├── SDL_yuv_sw_c.h │ │ │ ├── mmx.h │ │ │ ├── opengl/ │ │ │ │ ├── SDL_glfuncs.h │ │ │ │ ├── SDL_render_gl.c │ │ │ │ ├── SDL_shaders_gl.c │ │ │ │ └── SDL_shaders_gl.h │ │ │ ├── opengles/ │ │ │ │ ├── SDL_glesfuncs.h │ │ │ │ └── SDL_render_gles.c │ │ │ ├── opengles2/ │ │ │ │ ├── SDL_gles2funcs.h │ │ │ │ ├── SDL_render_gles2.c │ │ │ │ ├── SDL_shaders_gles2.c │ │ │ │ └── SDL_shaders_gles2.h │ │ │ ├── psp/ │ │ │ │ └── SDL_render_psp.c │ │ │ └── software/ │ │ │ ├── SDL_blendfillrect.c │ │ │ ├── SDL_blendfillrect.h │ │ │ ├── SDL_blendline.c │ │ │ ├── SDL_blendline.h │ │ │ ├── SDL_blendpoint.c │ │ │ ├── SDL_blendpoint.h │ │ │ ├── SDL_draw.h │ │ │ ├── SDL_drawline.c │ │ │ ├── SDL_drawline.h │ │ │ ├── SDL_drawpoint.c │ │ │ ├── SDL_drawpoint.h │ │ │ ├── SDL_render_sw.c │ │ │ ├── SDL_render_sw_c.h │ │ │ ├── SDL_rotate.c │ │ │ └── SDL_rotate.h │ │ ├── stdlib/ │ │ │ ├── SDL_getenv.c │ │ │ ├── SDL_iconv.c │ │ │ ├── SDL_malloc.c │ │ │ ├── SDL_qsort.c │ │ │ ├── SDL_stdlib.c │ │ │ └── SDL_string.c │ │ ├── test/ │ │ │ ├── SDL_test_assert.c │ │ │ ├── SDL_test_common.c │ │ │ ├── SDL_test_compare.c │ │ │ ├── SDL_test_crc32.c │ │ │ ├── SDL_test_font.c │ │ │ ├── SDL_test_fuzzer.c │ │ │ ├── SDL_test_harness.c │ │ │ ├── SDL_test_imageBlit.c │ │ │ ├── SDL_test_imageBlitBlend.c │ │ │ ├── SDL_test_imageFace.c │ │ │ ├── SDL_test_imagePrimitives.c │ │ │ ├── SDL_test_imagePrimitivesBlend.c │ │ │ ├── SDL_test_log.c │ │ │ ├── SDL_test_md5.c │ │ │ └── SDL_test_random.c │ │ ├── thread/ │ │ │ ├── SDL_systhread.h │ │ │ ├── SDL_thread.c │ │ │ ├── SDL_thread_c.h │ │ │ └── pthread/ │ │ │ ├── SDL_syscond.c │ │ │ ├── SDL_sysmutex.c │ │ │ ├── SDL_sysmutex_c.h │ │ │ ├── SDL_syssem.c │ │ │ ├── SDL_systhread.c │ │ │ ├── SDL_systhread_c.h │ │ │ └── SDL_systls.c │ │ ├── timer/ │ │ │ ├── SDL_timer.c │ │ │ ├── SDL_timer_c.h │ │ │ └── unix/ │ │ │ └── SDL_systimer.c │ │ └── video/ │ │ ├── SDL_RLEaccel.c │ │ ├── SDL_RLEaccel_c.h │ │ ├── SDL_blit.c │ │ ├── SDL_blit.h │ │ ├── SDL_blit_0.c │ │ ├── SDL_blit_1.c │ │ ├── SDL_blit_A.c │ │ ├── SDL_blit_N.c │ │ ├── SDL_blit_auto.c │ │ ├── SDL_blit_auto.h │ │ ├── SDL_blit_copy.c │ │ ├── SDL_blit_copy.h │ │ ├── SDL_blit_slow.c │ │ ├── SDL_blit_slow.h │ │ ├── SDL_bmp.c │ │ ├── SDL_clipboard.c │ │ ├── SDL_egl.c │ │ ├── SDL_egl_c.h │ │ ├── SDL_fillrect.c │ │ ├── SDL_pixels.c │ │ ├── SDL_pixels_c.h │ │ ├── SDL_rect.c │ │ ├── SDL_rect_c.h │ │ ├── SDL_shape.c │ │ ├── SDL_shape_internals.h │ │ ├── SDL_stretch.c │ │ ├── SDL_surface.c │ │ ├── SDL_sysvideo.h │ │ ├── SDL_video.c │ │ ├── android/ │ │ │ ├── SDL_androidclipboard.c │ │ │ ├── SDL_androidclipboard.h │ │ │ ├── SDL_androidevents.c │ │ │ ├── SDL_androidevents.h │ │ │ ├── SDL_androidgl.c │ │ │ ├── SDL_androidkeyboard.c │ │ │ ├── SDL_androidkeyboard.h │ │ │ ├── SDL_androidmessagebox.c │ │ │ ├── SDL_androidmessagebox.h │ │ │ ├── SDL_androidmouse.c │ │ │ ├── SDL_androidmouse.h │ │ │ ├── SDL_androidtouch.c │ │ │ ├── SDL_androidtouch.h │ │ │ ├── SDL_androidvideo.c │ │ │ ├── SDL_androidvideo.h │ │ │ ├── SDL_androidwindow.c │ │ │ └── SDL_androidwindow.h │ │ └── sdlgenblit.pl │ ├── SDL2_gfx/ │ │ ├── CMakeFiles/ │ │ │ └── SDL2_gfx.dir/ │ │ │ └── src/ │ │ │ ├── SDL2_framerate.c.o │ │ │ ├── SDL2_gfxPrimitives.c.o │ │ │ ├── SDL2_imageFilter.c.o │ │ │ └── SDL2_rotozoom.c.o │ │ ├── CMakeLists.txt │ │ ├── build.gradle │ │ ├── cmake_install.cmake │ │ ├── include/ │ │ │ ├── SDL2_framerate.h │ │ │ ├── SDL2_gfxPrimitives.h │ │ │ ├── SDL2_gfxPrimitives_font.h │ │ │ ├── SDL2_imageFilter.h │ │ │ └── SDL2_rotozoom.h │ │ └── src/ │ │ ├── SDL2_framerate.c │ │ ├── SDL2_gfxPrimitives.c │ │ ├── SDL2_imageFilter.c │ │ └── SDL2_rotozoom.c │ ├── SDL2_image/ │ │ ├── CMakeFiles/ │ │ │ └── SDL2_image.dir/ │ │ │ └── src/ │ │ │ ├── IMG.c.o │ │ │ ├── IMG_bmp.c.o │ │ │ ├── IMG_gif.c.o │ │ │ ├── IMG_jpg.c.o │ │ │ ├── IMG_lbm.c.o │ │ │ ├── IMG_pcx.c.o │ │ │ ├── IMG_png.c.o │ │ │ ├── IMG_pnm.c.o │ │ │ ├── IMG_tga.c.o │ │ │ ├── IMG_tif.c.o │ │ │ ├── IMG_webp.c.o │ │ │ ├── IMG_xcf.c.o │ │ │ ├── IMG_xpm.c.o │ │ │ ├── IMG_xv.c.o │ │ │ ├── IMG_xxx.c.o │ │ │ └── showimage.c.o │ │ ├── CMakeLists.txt │ │ ├── build.gradle │ │ ├── cmake_install.cmake │ │ ├── include/ │ │ │ ├── SDL_image.h │ │ │ └── miniz.h │ │ └── src/ │ │ ├── IMG.c │ │ ├── IMG_bmp.c │ │ ├── IMG_gif.c │ │ ├── IMG_jpg.c │ │ ├── IMG_lbm.c │ │ ├── IMG_pcx.c │ │ ├── IMG_png.c │ │ ├── IMG_pnm.c │ │ ├── IMG_tga.c │ │ ├── IMG_tif.c │ │ ├── IMG_webp.c │ │ ├── IMG_xcf.c │ │ ├── IMG_xpm.c │ │ ├── IMG_xv.c │ │ ├── IMG_xxx.c │ │ └── showimage.c │ ├── SDL2_jpeg/ │ │ ├── CMakeFiles/ │ │ │ └── SDL2_jpeg.dir/ │ │ │ └── src/ │ │ │ ├── cdjpeg.c.o │ │ │ ├── jaricom.c.o │ │ │ ├── jcapimin.c.o │ │ │ ├── jcapistd.c.o │ │ │ ├── jcarith.c.o │ │ │ ├── jccoefct.c.o │ │ │ ├── jccolor.c.o │ │ │ ├── jcdctmgr.c.o │ │ │ ├── jchuff.c.o │ │ │ ├── jcinit.c.o │ │ │ ├── jcmainct.c.o │ │ │ ├── jcmarker.c.o │ │ │ ├── jcmaster.c.o │ │ │ ├── jcomapi.c.o │ │ │ ├── jcparam.c.o │ │ │ ├── jcprepct.c.o │ │ │ ├── jcsample.c.o │ │ │ ├── jctrans.c.o │ │ │ ├── jdapimin.c.o │ │ │ ├── jdapistd.c.o │ │ │ ├── jdarith.c.o │ │ │ ├── jdatadst.c.o │ │ │ ├── jdatasrc.c.o │ │ │ ├── jdcoefct.c.o │ │ │ ├── jdcolor.c.o │ │ │ ├── jddctmgr.c.o │ │ │ ├── jdhuff.c.o │ │ │ ├── jdinput.c.o │ │ │ ├── jdmainct.c.o │ │ │ ├── jdmarker.c.o │ │ │ ├── jdmaster.c.o │ │ │ ├── jdmerge.c.o │ │ │ ├── jdpostct.c.o │ │ │ ├── jdsample.c.o │ │ │ ├── jdtrans.c.o │ │ │ ├── jerror.c.o │ │ │ ├── jfdctflt.c.o │ │ │ ├── jfdctfst.c.o │ │ │ ├── jfdctint.c.o │ │ │ ├── jidctflt.c.o │ │ │ ├── jidctfst.c.o │ │ │ ├── jidctint.c.o │ │ │ ├── jmem-android.c.o │ │ │ ├── jmemmgr.c.o │ │ │ ├── jquant1.c.o │ │ │ ├── jquant2.c.o │ │ │ ├── jutils.c.o │ │ │ ├── rdbmp.c.o │ │ │ ├── rdcolmap.c.o │ │ │ ├── rdgif.c.o │ │ │ ├── rdppm.c.o │ │ │ ├── rdrle.c.o │ │ │ ├── rdswitch.c.o │ │ │ ├── rdtarga.c.o │ │ │ ├── transupp.c.o │ │ │ ├── wrbmp.c.o │ │ │ ├── wrgif.c.o │ │ │ ├── wrjpgcom.c.o │ │ │ ├── wrppm.c.o │ │ │ ├── wrrle.c.o │ │ │ └── wrtarga.c.o │ │ ├── CMakeLists.txt │ │ ├── build.gradle │ │ ├── cmake_install.cmake │ │ ├── include/ │ │ │ ├── cderror.h │ │ │ ├── cdjpeg.h │ │ │ ├── jconfig.h │ │ │ ├── jdct.h │ │ │ ├── jerror.h │ │ │ ├── jinclude.h │ │ │ ├── jmemsys.h │ │ │ ├── jmorecfg.h │ │ │ ├── jpegint.h │ │ │ ├── jpeglib.h │ │ │ ├── jversion.h │ │ │ └── transupp.h │ │ └── src/ │ │ ├── cdjpeg.c │ │ ├── cjpeg.c │ │ ├── ckconfig.c │ │ ├── djpeg.c │ │ ├── example.c │ │ ├── jaricom.c │ │ ├── jcapimin.c │ │ ├── jcapistd.c │ │ ├── jcarith.c │ │ ├── jccoefct.c │ │ ├── jccolor.c │ │ ├── jcdctmgr.c │ │ ├── jchuff.c │ │ ├── jcinit.c │ │ ├── jcmainct.c │ │ ├── jcmarker.c │ │ ├── jcmaster.c │ │ ├── jcomapi.c │ │ ├── jcparam.c │ │ ├── jcprepct.c │ │ ├── jcsample.c │ │ ├── jctrans.c │ │ ├── jdapimin.c │ │ ├── jdapistd.c │ │ ├── jdarith.c │ │ ├── jdatadst.c │ │ ├── jdatasrc.c │ │ ├── jdcoefct.c │ │ ├── jdcolor.c │ │ ├── jddctmgr.c │ │ ├── jdhuff.c │ │ ├── jdinput.c │ │ ├── jdmainct.c │ │ ├── jdmarker.c │ │ ├── jdmaster.c │ │ ├── jdmerge.c │ │ ├── jdpostct.c │ │ ├── jdsample.c │ │ ├── jdtrans.c │ │ ├── jerror.c │ │ ├── jfdctflt.c │ │ ├── jfdctfst.c │ │ ├── jfdctint.c │ │ ├── jidctflt.c │ │ ├── jidctfst.c │ │ ├── jidctint.c │ │ ├── jmem-android.c │ │ ├── jmemansi.c │ │ ├── jmemdos.c │ │ ├── jmemmac.c │ │ ├── jmemmgr.c │ │ ├── jmemname.c │ │ ├── jmemnobs.c │ │ ├── jpegtran.c │ │ ├── jquant1.c │ │ ├── jquant2.c │ │ ├── jutils.c │ │ ├── rdbmp.c │ │ ├── rdcolmap.c │ │ ├── rdgif.c │ │ ├── rdjpgcom.c │ │ ├── rdppm.c │ │ ├── rdrle.c │ │ ├── rdswitch.c │ │ ├── rdtarga.c │ │ ├── transupp.c │ │ ├── wrbmp.c │ │ ├── wrgif.c │ │ ├── wrjpgcom.c │ │ ├── wrppm.c │ │ ├── wrrle.c │ │ └── wrtarga.c │ ├── SDL2_mixer/ │ │ ├── CMakeFiles/ │ │ │ └── SDL2_mixer.dir/ │ │ │ └── src/ │ │ │ ├── dynamic_flac.c.o │ │ │ ├── dynamic_fluidsynth.c.o │ │ │ ├── dynamic_mod.c.o │ │ │ ├── dynamic_modplug.c.o │ │ │ ├── dynamic_mp3.c.o │ │ │ ├── dynamic_ogg.c.o │ │ │ ├── effect_position.c.o │ │ │ ├── effect_stereoreverse.c.o │ │ │ ├── effects_internal.c.o │ │ │ ├── fluidsynth.c.o │ │ │ ├── load_aiff.c.o │ │ │ ├── load_flac.c.o │ │ │ ├── load_mp3.c.o │ │ │ ├── load_ogg.c.o │ │ │ ├── load_voc.c.o │ │ │ ├── mixer.c.o │ │ │ ├── music.c.o │ │ │ ├── music_cmd.c.o │ │ │ ├── music_flac.c.o │ │ │ ├── music_mad.c.o │ │ │ ├── music_mod.c.o │ │ │ ├── music_modplug.c.o │ │ │ ├── music_ogg.c.o │ │ │ ├── playmus.c.o │ │ │ └── wavestream.c.o │ │ ├── CMakeLists.txt │ │ ├── build.gradle │ │ ├── cmake_install.cmake │ │ ├── include/ │ │ │ ├── SDL_mixer.h │ │ │ ├── dynamic_flac.h │ │ │ ├── dynamic_fluidsynth.h │ │ │ ├── dynamic_mod.h │ │ │ ├── dynamic_modplug.h │ │ │ ├── dynamic_mp3.h │ │ │ ├── dynamic_ogg.h │ │ │ ├── effects_internal.h │ │ │ ├── fluidsynth.h │ │ │ ├── load_aiff.h │ │ │ ├── load_flac.h │ │ │ ├── load_mp3.h │ │ │ ├── load_ogg.h │ │ │ ├── load_voc.h │ │ │ ├── music_cmd.h │ │ │ ├── music_flac.h │ │ │ ├── music_mad.h │ │ │ ├── music_mod.h │ │ │ ├── music_modplug.h │ │ │ ├── music_ogg.h │ │ │ └── wavestream.h │ │ └── src/ │ │ ├── dynamic_flac.c │ │ ├── dynamic_fluidsynth.c │ │ ├── dynamic_mod.c │ │ ├── dynamic_modplug.c │ │ ├── dynamic_mp3.c │ │ ├── dynamic_ogg.c │ │ ├── effect_position.c │ │ ├── effect_stereoreverse.c │ │ ├── effects_internal.c │ │ ├── fluidsynth.c │ │ ├── load_aiff.c │ │ ├── load_flac.c │ │ ├── load_mp3.c │ │ ├── load_ogg.c │ │ ├── load_voc.c │ │ ├── mixer.c │ │ ├── music.c │ │ ├── music_cmd.c │ │ ├── music_flac.c │ │ ├── music_mad.c │ │ ├── music_mod.c │ │ ├── music_modplug.c │ │ ├── music_ogg.c │ │ ├── playmus.c │ │ ├── playwave.c │ │ └── wavestream.c │ ├── SDL2_png/ │ │ ├── CMakeFiles/ │ │ │ └── SDL2_png.dir/ │ │ │ └── src/ │ │ │ ├── png.c.o │ │ │ ├── pngerror.c.o │ │ │ ├── pngget.c.o │ │ │ ├── pngmem.c.o │ │ │ ├── pngpread.c.o │ │ │ ├── pngread.c.o │ │ │ ├── pngrio.c.o │ │ │ ├── pngrtran.c.o │ │ │ ├── pngrutil.c.o │ │ │ ├── pngset.c.o │ │ │ ├── pngtrans.c.o │ │ │ ├── pngwio.c.o │ │ │ ├── pngwrite.c.o │ │ │ ├── pngwtran.c.o │ │ │ └── pngwutil.c.o │ │ ├── CMakeLists.txt │ │ ├── build.gradle │ │ ├── cmake_install.cmake │ │ ├── include/ │ │ │ ├── config.h │ │ │ ├── png.h │ │ │ ├── pngconf.h │ │ │ ├── pngdebug.h │ │ │ ├── pnginfo.h │ │ │ ├── pnglibconf.h │ │ │ ├── pngprefix.h │ │ │ ├── pngpriv.h │ │ │ └── pngstruct.h │ │ └── src/ │ │ ├── png.c │ │ ├── pngerror.c │ │ ├── pngget.c │ │ ├── pngmem.c │ │ ├── pngpread.c │ │ ├── pngread.c │ │ ├── pngrio.c │ │ ├── pngrtran.c │ │ ├── pngrutil.c │ │ ├── pngset.c │ │ ├── pngtest.c │ │ ├── pngtrans.c │ │ ├── pngwio.c │ │ ├── pngwrite.c │ │ ├── pngwtran.c │ │ └── pngwutil.c │ ├── SDL2_ttf/ │ │ ├── CMakeFiles/ │ │ │ └── SDL2_ttf.dir/ │ │ │ └── src/ │ │ │ ├── SDL_ttf.c.o │ │ │ └── glfont.c.o │ │ ├── CMakeLists.txt │ │ ├── build.gradle │ │ ├── cmake_install.cmake │ │ ├── include/ │ │ │ └── SDL_ttf.h │ │ └── src/ │ │ ├── SDL_ttf.c │ │ ├── glfont.c │ │ └── showfont.c │ └── freetype/ │ ├── CMakeFiles/ │ │ └── freetype.dir/ │ │ └── src/ │ │ ├── base/ │ │ │ ├── ftapi.c.o │ │ │ ├── ftbase.c.o │ │ │ ├── ftbbox.c.o │ │ │ ├── ftbdf.c.o │ │ │ ├── ftbitmap.c.o │ │ │ ├── ftcid.c.o │ │ │ ├── ftdebug.c.o │ │ │ ├── ftfstype.c.o │ │ │ ├── ftgasp.c.o │ │ │ ├── ftglyph.c.o │ │ │ ├── ftgxval.c.o │ │ │ ├── ftinit.c.o │ │ │ ├── ftlcdfil.c.o │ │ │ ├── ftmm.c.o │ │ │ ├── ftotval.c.o │ │ │ ├── ftpatent.c.o │ │ │ ├── ftpfr.c.o │ │ │ ├── ftstroke.c.o │ │ │ ├── ftsynth.c.o │ │ │ ├── ftsystem.c.o │ │ │ ├── fttype1.c.o │ │ │ ├── ftwinfnt.c.o │ │ │ ├── ftxf86.c.o │ │ │ └── md5.c.o │ │ ├── bzip2/ │ │ │ └── ftbzip2.c.o │ │ ├── raster/ │ │ │ ├── ftraster.c.o │ │ │ ├── ftrend1.c.o │ │ │ └── rastpic.c.o │ │ ├── sfnt/ │ │ │ ├── sfdriver.c.o │ │ │ ├── sfntpic.c.o │ │ │ ├── sfobjs.c.o │ │ │ ├── ttbdf.c.o │ │ │ ├── ttcmap.c.o │ │ │ ├── ttkern.c.o │ │ │ ├── ttload.c.o │ │ │ ├── ttmtx.c.o │ │ │ ├── ttpost.c.o │ │ │ └── ttsbit.c.o │ │ ├── smooth/ │ │ │ ├── ftgrays.c.o │ │ │ ├── ftsmooth.c.o │ │ │ └── ftspic.c.o │ │ └── truetype/ │ │ ├── ttdriver.c.o │ │ ├── ttgload.c.o │ │ ├── ttgxvar.c.o │ │ ├── ttinterp.c.o │ │ ├── ttobjs.c.o │ │ ├── ttpic.c.o │ │ ├── ttpload.c.o │ │ └── ttsubpix.c.o │ ├── CMakeLists.txt │ ├── build.gradle │ ├── cmake_install.cmake │ ├── include/ │ │ ├── freetype/ │ │ │ ├── config/ │ │ │ │ ├── ftconfig.h │ │ │ │ ├── ftheader.h │ │ │ │ ├── ftmodule.h │ │ │ │ ├── ftoption.h │ │ │ │ └── ftstdlib.h │ │ │ ├── freetype.h │ │ │ ├── ftadvanc.h │ │ │ ├── ftautoh.h │ │ │ ├── ftbbox.h │ │ │ ├── ftbdf.h │ │ │ ├── ftbitmap.h │ │ │ ├── ftbzip2.h │ │ │ ├── ftcache.h │ │ │ ├── ftcffdrv.h │ │ │ ├── ftchapters.h │ │ │ ├── ftcid.h │ │ │ ├── fterrdef.h │ │ │ ├── fterrors.h │ │ │ ├── ftgasp.h │ │ │ ├── ftglyph.h │ │ │ ├── ftgxval.h │ │ │ ├── ftgzip.h │ │ │ ├── ftimage.h │ │ │ ├── ftincrem.h │ │ │ ├── ftlcdfil.h │ │ │ ├── ftlist.h │ │ │ ├── ftlzw.h │ │ │ ├── ftmac.h │ │ │ ├── ftmm.h │ │ │ ├── ftmodapi.h │ │ │ ├── ftmoderr.h │ │ │ ├── ftotval.h │ │ │ ├── ftoutln.h │ │ │ ├── ftpfr.h │ │ │ ├── ftrender.h │ │ │ ├── ftsizes.h │ │ │ ├── ftsnames.h │ │ │ ├── ftstroke.h │ │ │ ├── ftsynth.h │ │ │ ├── ftsystem.h │ │ │ ├── fttrigon.h │ │ │ ├── fttypes.h │ │ │ ├── ftwinfnt.h │ │ │ ├── ftxf86.h │ │ │ ├── internal/ │ │ │ │ ├── autohint.h │ │ │ │ ├── ftcalc.h │ │ │ │ ├── ftdebug.h │ │ │ │ ├── ftdriver.h │ │ │ │ ├── ftgloadr.h │ │ │ │ ├── ftmemory.h │ │ │ │ ├── ftobjs.h │ │ │ │ ├── ftpic.h │ │ │ │ ├── ftrfork.h │ │ │ │ ├── ftserv.h │ │ │ │ ├── ftstream.h │ │ │ │ ├── fttrace.h │ │ │ │ ├── ftvalid.h │ │ │ │ ├── internal.h │ │ │ │ ├── psaux.h │ │ │ │ ├── pshints.h │ │ │ │ ├── services/ │ │ │ │ │ ├── svbdf.h │ │ │ │ │ ├── svcid.h │ │ │ │ │ ├── svgldict.h │ │ │ │ │ ├── svgxval.h │ │ │ │ │ ├── svkern.h │ │ │ │ │ ├── svmm.h │ │ │ │ │ ├── svotval.h │ │ │ │ │ ├── svpfr.h │ │ │ │ │ ├── svpostnm.h │ │ │ │ │ ├── svprop.h │ │ │ │ │ ├── svpscmap.h │ │ │ │ │ ├── svpsinfo.h │ │ │ │ │ ├── svsfnt.h │ │ │ │ │ ├── svttcmap.h │ │ │ │ │ ├── svtteng.h │ │ │ │ │ ├── svttglyf.h │ │ │ │ │ ├── svwinfnt.h │ │ │ │ │ └── svxf86nm.h │ │ │ │ ├── sfnt.h │ │ │ │ ├── t1types.h │ │ │ │ └── tttypes.h │ │ │ ├── t1tables.h │ │ │ ├── ttnameid.h │ │ │ ├── tttables.h │ │ │ ├── tttags.h │ │ │ └── ttunpat.h │ │ ├── ft2build.h │ │ ├── ftconfig.h │ │ └── ftmodule.h │ └── src/ │ ├── Jamfile │ ├── autofit/ │ │ ├── Jamfile │ │ ├── afangles.c │ │ ├── afangles.h │ │ ├── afcjk.c │ │ ├── afcjk.h │ │ ├── afdummy.c │ │ ├── afdummy.h │ │ ├── aferrors.h │ │ ├── afglobal.c │ │ ├── afglobal.h │ │ ├── afhints.c │ │ ├── afhints.h │ │ ├── afindic.c │ │ ├── afindic.h │ │ ├── aflatin.c │ │ ├── aflatin.h │ │ ├── aflatin2.c │ │ ├── aflatin2.h │ │ ├── afloader.c │ │ ├── afloader.h │ │ ├── afmodule.c │ │ ├── afmodule.h │ │ ├── afpic.c │ │ ├── afpic.h │ │ ├── aftypes.h │ │ ├── afwarp.c │ │ ├── afwarp.h │ │ ├── autofit.c │ │ ├── module.mk │ │ └── rules.mk │ ├── base/ │ │ ├── Jamfile │ │ ├── basepic.c │ │ ├── basepic.h │ │ ├── ftadvanc.c │ │ ├── ftapi.c │ │ ├── ftbase.c │ │ ├── ftbase.h │ │ ├── ftbbox.c │ │ ├── ftbdf.c │ │ ├── ftbitmap.c │ │ ├── ftcalc.c │ │ ├── ftcid.c │ │ ├── ftdbgmem.c │ │ ├── ftdebug.c │ │ ├── ftfstype.c │ │ ├── ftgasp.c │ │ ├── ftgloadr.c │ │ ├── ftglyph.c │ │ ├── ftgxval.c │ │ ├── ftinit.c │ │ ├── ftlcdfil.c │ │ ├── ftmac.c │ │ ├── ftmm.c │ │ ├── ftobjs.c │ │ ├── ftotval.c │ │ ├── ftoutln.c │ │ ├── ftpatent.c │ │ ├── ftpfr.c │ │ ├── ftpic.c │ │ ├── ftrfork.c │ │ ├── ftsnames.c │ │ ├── ftstream.c │ │ ├── ftstroke.c │ │ ├── ftsynth.c │ │ ├── ftsystem.c │ │ ├── fttrigon.c │ │ ├── fttype1.c │ │ ├── ftutil.c │ │ ├── ftwinfnt.c │ │ ├── ftxf86.c │ │ ├── md5.c │ │ ├── md5.h │ │ └── rules.mk │ ├── bdf/ │ │ ├── Jamfile │ │ ├── README │ │ ├── bdf.c │ │ ├── bdf.h │ │ ├── bdfdrivr.c │ │ ├── bdfdrivr.h │ │ ├── bdferror.h │ │ ├── bdflib.c │ │ ├── module.mk │ │ └── rules.mk │ ├── bzip2/ │ │ ├── Jamfile │ │ ├── ftbzip2.c │ │ └── rules.mk │ ├── cache/ │ │ ├── Jamfile │ │ ├── ftcache.c │ │ ├── ftcbasic.c │ │ ├── ftccache.c │ │ ├── ftccache.h │ │ ├── ftccback.h │ │ ├── ftccmap.c │ │ ├── ftcerror.h │ │ ├── ftcglyph.c │ │ ├── ftcglyph.h │ │ ├── ftcimage.c │ │ ├── ftcimage.h │ │ ├── ftcmanag.c │ │ ├── ftcmanag.h │ │ ├── ftcmru.c │ │ ├── ftcmru.h │ │ ├── ftcsbits.c │ │ ├── ftcsbits.h │ │ └── rules.mk │ ├── cff/ │ │ ├── Jamfile │ │ ├── cf2arrst.c │ │ ├── cf2arrst.h │ │ ├── cf2blues.c │ │ ├── cf2blues.h │ │ ├── cf2error.c │ │ ├── cf2error.h │ │ ├── cf2fixed.h │ │ ├── cf2font.c │ │ ├── cf2font.h │ │ ├── cf2ft.c │ │ ├── cf2ft.h │ │ ├── cf2glue.h │ │ ├── cf2hints.c │ │ ├── cf2hints.h │ │ ├── cf2intrp.c │ │ ├── cf2intrp.h │ │ ├── cf2read.c │ │ ├── cf2read.h │ │ ├── cf2stack.c │ │ ├── cf2stack.h │ │ ├── cf2types.h │ │ ├── cff.c │ │ ├── cffcmap.c │ │ ├── cffcmap.h │ │ ├── cffdrivr.c │ │ ├── cffdrivr.h │ │ ├── cfferrs.h │ │ ├── cffgload.c │ │ ├── cffgload.h │ │ ├── cffload.c │ │ ├── cffload.h │ │ ├── cffobjs.c │ │ ├── cffobjs.h │ │ ├── cffparse.c │ │ ├── cffparse.h │ │ ├── cffpic.c │ │ ├── cffpic.h │ │ ├── cfftoken.h │ │ ├── cfftypes.h │ │ ├── module.mk │ │ └── rules.mk │ ├── cid/ │ │ ├── Jamfile │ │ ├── ciderrs.h │ │ ├── cidgload.c │ │ ├── cidgload.h │ │ ├── cidload.c │ │ ├── cidload.h │ │ ├── cidobjs.c │ │ ├── cidobjs.h │ │ ├── cidparse.c │ │ ├── cidparse.h │ │ ├── cidriver.c │ │ ├── cidriver.h │ │ ├── cidtoken.h │ │ ├── module.mk │ │ ├── rules.mk │ │ └── type1cid.c │ ├── gxvalid/ │ │ ├── Jamfile │ │ ├── README │ │ ├── gxvalid.c │ │ ├── gxvalid.h │ │ ├── gxvbsln.c │ │ ├── gxvcommn.c │ │ ├── gxvcommn.h │ │ ├── gxverror.h │ │ ├── gxvfeat.c │ │ ├── gxvfeat.h │ │ ├── gxvfgen.c │ │ ├── gxvjust.c │ │ ├── gxvkern.c │ │ ├── gxvlcar.c │ │ ├── gxvmod.c │ │ ├── gxvmod.h │ │ ├── gxvmort.c │ │ ├── gxvmort.h │ │ ├── gxvmort0.c │ │ ├── gxvmort1.c │ │ ├── gxvmort2.c │ │ ├── gxvmort4.c │ │ ├── gxvmort5.c │ │ ├── gxvmorx.c │ │ ├── gxvmorx.h │ │ ├── gxvmorx0.c │ │ ├── gxvmorx1.c │ │ ├── gxvmorx2.c │ │ ├── gxvmorx4.c │ │ ├── gxvmorx5.c │ │ ├── gxvopbd.c │ │ ├── gxvprop.c │ │ ├── gxvtrak.c │ │ ├── module.mk │ │ └── rules.mk │ ├── gzip/ │ │ ├── Jamfile │ │ ├── adler32.c │ │ ├── ftgzip.c │ │ ├── infblock.c │ │ ├── infblock.h │ │ ├── infcodes.c │ │ ├── infcodes.h │ │ ├── inffixed.h │ │ ├── inflate.c │ │ ├── inftrees.c │ │ ├── inftrees.h │ │ ├── infutil.c │ │ ├── infutil.h │ │ ├── rules.mk │ │ ├── zconf.h │ │ ├── zlib.h │ │ ├── zutil.c │ │ └── zutil.h │ ├── lzw/ │ │ ├── Jamfile │ │ ├── ftlzw.c │ │ ├── ftzopen.c │ │ ├── ftzopen.h │ │ └── rules.mk │ ├── otvalid/ │ │ ├── Jamfile │ │ ├── module.mk │ │ ├── otvalid.c │ │ ├── otvalid.h │ │ ├── otvbase.c │ │ ├── otvcommn.c │ │ ├── otvcommn.h │ │ ├── otverror.h │ │ ├── otvgdef.c │ │ ├── otvgpos.c │ │ ├── otvgpos.h │ │ ├── otvgsub.c │ │ ├── otvjstf.c │ │ ├── otvmath.c │ │ ├── otvmod.c │ │ ├── otvmod.h │ │ └── rules.mk │ ├── pcf/ │ │ ├── Jamfile │ │ ├── README │ │ ├── module.mk │ │ ├── pcf.c │ │ ├── pcf.h │ │ ├── pcfdrivr.c │ │ ├── pcfdrivr.h │ │ ├── pcferror.h │ │ ├── pcfread.c │ │ ├── pcfread.h │ │ ├── pcfutil.c │ │ ├── pcfutil.h │ │ └── rules.mk │ ├── pfr/ │ │ ├── Jamfile │ │ ├── module.mk │ │ ├── pfr.c │ │ ├── pfrcmap.c │ │ ├── pfrcmap.h │ │ ├── pfrdrivr.c │ │ ├── pfrdrivr.h │ │ ├── pfrerror.h │ │ ├── pfrgload.c │ │ ├── pfrgload.h │ │ ├── pfrload.c │ │ ├── pfrload.h │ │ ├── pfrobjs.c │ │ ├── pfrobjs.h │ │ ├── pfrsbit.c │ │ ├── pfrsbit.h │ │ ├── pfrtypes.h │ │ └── rules.mk │ ├── psaux/ │ │ ├── Jamfile │ │ ├── afmparse.c │ │ ├── afmparse.h │ │ ├── module.mk │ │ ├── psaux.c │ │ ├── psauxerr.h │ │ ├── psauxmod.c │ │ ├── psauxmod.h │ │ ├── psconv.c │ │ ├── psconv.h │ │ ├── psobjs.c │ │ ├── psobjs.h │ │ ├── rules.mk │ │ ├── t1cmap.c │ │ ├── t1cmap.h │ │ ├── t1decode.c │ │ └── t1decode.h │ ├── pshinter/ │ │ ├── Jamfile │ │ ├── module.mk │ │ ├── pshalgo.c │ │ ├── pshalgo.h │ │ ├── pshglob.c │ │ ├── pshglob.h │ │ ├── pshinter.c │ │ ├── pshmod.c │ │ ├── pshmod.h │ │ ├── pshnterr.h │ │ ├── pshpic.c │ │ ├── pshpic.h │ │ ├── pshrec.c │ │ ├── pshrec.h │ │ └── rules.mk │ ├── psnames/ │ │ ├── Jamfile │ │ ├── module.mk │ │ ├── psmodule.c │ │ ├── psmodule.h │ │ ├── psnamerr.h │ │ ├── psnames.c │ │ ├── pspic.c │ │ ├── pspic.h │ │ ├── pstables.h │ │ └── rules.mk │ ├── raster/ │ │ ├── Jamfile │ │ ├── ftmisc.h │ │ ├── ftraster.c │ │ ├── ftraster.h │ │ ├── ftrend1.c │ │ ├── ftrend1.h │ │ ├── module.mk │ │ ├── raster.c │ │ ├── rasterrs.h │ │ ├── rastpic.c │ │ ├── rastpic.h │ │ └── rules.mk │ ├── sfnt/ │ │ ├── Jamfile │ │ ├── module.mk │ │ ├── rules.mk │ │ ├── sfdriver.c │ │ ├── sfdriver.h │ │ ├── sferrors.h │ │ ├── sfnt.c │ │ ├── sfntpic.c │ │ ├── sfntpic.h │ │ ├── sfobjs.c │ │ ├── sfobjs.h │ │ ├── ttbdf.c │ │ ├── ttbdf.h │ │ ├── ttcmap.c │ │ ├── ttcmap.h │ │ ├── ttcmapc.h │ │ ├── ttkern.c │ │ ├── ttkern.h │ │ ├── ttload.c │ │ ├── ttload.h │ │ ├── ttmtx.c │ │ ├── ttmtx.h │ │ ├── ttpost.c │ │ ├── ttpost.h │ │ ├── ttsbit.c │ │ ├── ttsbit.h │ │ └── ttsbit0.c │ ├── smooth/ │ │ ├── Jamfile │ │ ├── ftgrays.c │ │ ├── ftgrays.h │ │ ├── ftsmerrs.h │ │ ├── ftsmooth.c │ │ ├── ftsmooth.h │ │ ├── ftspic.c │ │ ├── ftspic.h │ │ ├── module.mk │ │ ├── rules.mk │ │ └── smooth.c │ ├── tools/ │ │ ├── Jamfile │ │ ├── apinames.c │ │ ├── chktrcmp.py │ │ ├── cordic.py │ │ ├── docmaker/ │ │ │ ├── content.py │ │ │ ├── docbeauty.py │ │ │ ├── docmaker.py │ │ │ ├── formatter.py │ │ │ ├── sources.py │ │ │ ├── tohtml.py │ │ │ └── utils.py │ │ ├── ftrandom/ │ │ │ ├── Makefile │ │ │ ├── README │ │ │ └── ftrandom.c │ │ ├── glnames.py │ │ ├── test_afm.c │ │ ├── test_bbox.c │ │ └── test_trig.c │ ├── truetype/ │ │ ├── Jamfile │ │ ├── module.mk │ │ ├── rules.mk │ │ ├── truetype.c │ │ ├── ttdriver.c │ │ ├── ttdriver.h │ │ ├── tterrors.h │ │ ├── ttgload.c │ │ ├── ttgload.h │ │ ├── ttgxvar.c │ │ ├── ttgxvar.h │ │ ├── ttinterp.c │ │ ├── ttinterp.h │ │ ├── ttobjs.c │ │ ├── ttobjs.h │ │ ├── ttpic.c │ │ ├── ttpic.h │ │ ├── ttpload.c │ │ ├── ttpload.h │ │ ├── ttsubpix.c │ │ └── ttsubpix.h │ ├── type1/ │ │ ├── Jamfile │ │ ├── module.mk │ │ ├── rules.mk │ │ ├── t1afm.c │ │ ├── t1afm.h │ │ ├── t1driver.c │ │ ├── t1driver.h │ │ ├── t1errors.h │ │ ├── t1gload.c │ │ ├── t1gload.h │ │ ├── t1load.c │ │ ├── t1load.h │ │ ├── t1objs.c │ │ ├── t1objs.h │ │ ├── t1parse.c │ │ ├── t1parse.h │ │ ├── t1tokens.h │ │ └── type1.c │ ├── type42/ │ │ ├── Jamfile │ │ ├── module.mk │ │ ├── rules.mk │ │ ├── t42drivr.c │ │ ├── t42drivr.h │ │ ├── t42error.h │ │ ├── t42objs.c │ │ ├── t42objs.h │ │ ├── t42parse.c │ │ ├── t42parse.h │ │ ├── t42types.h │ │ └── type42.c │ └── winfonts/ │ ├── Jamfile │ ├── fnterrs.h │ ├── module.mk │ ├── rules.mk │ ├── winfnt.c │ └── winfnt.h ├── local.properties ├── open_codeblocks.bat ├── open_codeblocks_sdl.bat ├── open_qt_creator.bat ├── open_vscode.bat └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gradle/5.1.1/gc.properties ================================================ ================================================ FILE: .gradle/buildOutputCleanup/cache.properties ================================================ #Tue Mar 02 01:28:27 UTC 2021 gradle.version=5.1.1 ================================================ FILE: .gradle/vcs-1/gc.properties ================================================ ================================================ FILE: .idea/.gitignore ================================================ # Default ignored files /shelf/ /workspace.xml ================================================ FILE: .idea/codeStyles/Project.xml ================================================
xmlns:android ^$
xmlns:.* ^$ BY_NAME
.*:id http://schemas.android.com/apk/res/android
.*:name http://schemas.android.com/apk/res/android
name ^$
style ^$
.* ^$ BY_NAME
.* http://schemas.android.com/apk/res/android ANDROID_ATTRIBUTE_ORDER
.* .* BY_NAME
================================================ FILE: .idea/compiler.xml ================================================ ================================================ FILE: .idea/encodings.xml ================================================ ================================================ FILE: .idea/gradle.xml ================================================ ================================================ FILE: .idea/is-Engine.iml ================================================ ================================================ FILE: .idea/jarRepositories.xml ================================================ ================================================ FILE: .idea/misc.xml ================================================ ================================================ FILE: .idea/modules/app/is-Engine.app.iml ================================================ ================================================ FILE: .idea/modules/is-Engine.iml ================================================ ================================================ FILE: .idea/modules.xml ================================================ ================================================ FILE: .idea/runConfigurations.xml ================================================ ================================================ FILE: .idea/vcs.xml ================================================ ================================================ FILE: CMakeLists.txt ================================================ cmake_minimum_required (VERSION 3.1) project(isengine) set(ISENGINE_PC true) # This confirms that we are using is::Engine to develop on PC (Windows / Linux) set(ISENGINE_MAIN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/app/src/main") set(ISENGINE_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/app/src/main/cpp") set(ISENGINE_CMAKE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/app/src/main/cmake") set(CMAKE_CXX_STANDARD 17) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY bin) # SFML on Windows OS if (WIN32) set (SFML_DIR "C:/SFML/lib/cmake/SFML") set(SFML_STATIC_LIBRARIES TRUE) # OpenAL lib file(COPY "C:/SFML/bin/openal32.dll" DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) endif() # Find SFML find_package(SFML REQUIRED COMPONENTS system window graphics network audio) include(${ISENGINE_CMAKE_DIR}/isengine.cmake) include(${ISENGINE_CMAKE_DIR}/app_src.cmake) # game resources files (image, sound, music, ...) file(COPY ${ISENGINE_MAIN_DIR}/assets DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) add_executable(isengine ${isengine} ${app_src} ${ISENGINE_CMAKE_DIR}/resource.rc # application icon ) target_include_directories(isengine PRIVATE include) target_link_libraries(isengine sfml-system sfml-window sfml-graphics sfml-network sfml-audio) ================================================ FILE: LICENSE.md ================================================ # zlib license 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. ================================================ FILE: README.md ================================================ ![header](./images/is_Engine_logo.png) ---------------------------- # is::Engine (Infinity Solutions::Engine) 4.0.1 Open source C++ framework which uses the mechanisms of **SFML 2** and which also allows to develop with several libraries at the same time **(SDL 2, Emscripten)** in order to easily export your games / applications on the **Nintendo Switch, Web (HTML 5), Mobile** and **PC (Windows, Linux, macOS)**. ## Contents - [Features](#features) - [What's new in this version](#whats-new-in-this-version) - [Extras](#extras) - [Game Engine User Guide and Youtube Tutorial](#game-engine-user-guide-and-youtube-tutorial) - [Example of a project created with the engine](#example-of-a-project-created-with-the-engine) - [Hello Scene Example](#hello-scene-example) - [Prerequisites](#prerequisites) - [How to use is::Engine with the different development tools](#how-to-use-isengine-with-the-different-development-tools) - [Description of the project structure](#description-of-the-project-structure) - [How to update an is::Engine project](#how-to-update-an-isengine-project) - [Special things to know about the engine](#special-things-to-know-about-the-engine) - [How to activate the use of Admob](#how-to-activate-the-use-of-admob) - [Contribute](#contribute) ## [![SFML logo](https://www.sfml-dev.org/images/logo.png)](https://www.sfml-dev.org) [![SDL](https://i48.servimg.com/u/f48/20/16/75/27/sdl_li10.png)](https://www.libsdl.org/) ![Web](https://i48.servimg.com/u/f48/20/16/75/27/web_lo10.png) [![Box2D Logo](https://box2d.org/images/logo.svg)](https://github.com/erincatto/box2d) [![Tiled Logo](https://i.servimg.com/u/f48/20/16/75/27/tiled_10.png)](https://www.mapeditor.org) [![Admob Logo](https://i48.servimg.com/u/f48/20/16/75/27/admob_10.png)](https://admob.google.com/) [![Tiny File Dialog](https://a.fsdn.com/allura/p/tinyfiledialogs/icon?1582196333?&w=90)](https://github.com/native-toolkit/tinyfiledialogs) [![VS logo](https://i48.servimg.com/u/f48/20/16/75/27/vs_ima12.png)](https://visualstudio.microsoft.com/fr/vs/community/) ## Features - Run SFML game / application on SDL 2 (Like an Emulator) - Language manager (English and French language support by default) - Scene System - Automatic management of a window - Multi support for development tools ([Nintendo-Switch](#-nintendo-switch), [Android Studio](#-android-studio), [Qt](#-qt), [CMake](#-cmake), [Emscripten](#-web-html-5---css-3), [Visual Studio Code](#-visual-studio-code), [Code::Block](#-codeblocks), [Visual Studio](#-visual-studio) - [is::LibConnect](#islibconnect) - [Web Push Notification](#-web-push-notification) - SDM (Step and Draw Manager) - GSM (Game Sound Manager) - GRM (Graphics Resources Manager) - CFF (CMake Files Fusion) - [TMX Lite](https://github.com/Is-Daouda/is-Engine-Example-Pack/tree/main/is-Engine-TMXLite) - [TMX Loader (Old version)](https://github.com/Is-Daouda/is-Engine-TMXLoader) - Entity system - Object Event System - [Button System](https://github.com/Is-Daouda/is-Engine-Example-Pack/tree/main/is-Engine-TinyFileDialog) - Background System - [Game Slider](https://github.com/Is-Daouda/is-Engine-Example-Pack/tree/main/is-Engine-GameSlider) - [Sprite Animation](https://github.com/Is-Daouda/is-Engine-Example-Pack/tree/main/is-Engine-GameSlider) - Basic collision engine - [2D physic engine (Box 2D)](https://github.com/Is-Daouda/is-Engine-Example-Pack/tree/main/is-Engine-Box2D) - Message Box System (Modifiable appearance via Sprites and Font) - Dialog Box System (as for RPG games) - [[Windows, Linux] Tiny File Dialogs to manage the dialog boxes of type: Message, File Save, Load File, Folder Selection](https://github.com/Is-Daouda/is-Engine-Example-Pack/tree/main/is-Engine-TinyFileDialog) - [Game Configuration System (sound, music, language, keyboard key, file path, ...)](./app/src/main/cpp/app_src/config/GameConfig.h) - Game Save System - [[Android] Virtual Game Pad with 6 keys (multi directional cross and A - B button)](https://github.com/Is-Daouda/is-Engine-Example-Pack/tree/main/is-Engine-VirtualGamePad) - [[Android] Virtual Game Pad Configuration (Adjust Position, transparency, ...)](https://github.com/Is-Daouda/is-Engine-Example-Pack/tree/main/is-Engine-VirtualGamePad) - [[Android] Show Ad Banner](https://github.com/Is-Daouda/is-Engine-Example-Pack/tree/main/is-Engine-Admob) - [[Android] Show Reward Video](https://github.com/Is-Daouda/is-Engine-Example-Pack/tree/main/is-Engine-Admob) ## What's new in this version - [Using Visual Studio and SDL 2 to develop with SFML.](#-visual-studio-sdl-2) - Bug Fixes ### 4.0.1 - Nintendo Switch Support - The **[Demo](https://github.com/Is-Daouda/is-Engine-Demo)** and **[tutorials](https://github.com/Is-Daouda/is-Engine-Tutorial)** are compatible with the Nintendo Switch. - Visual Studio 2022 can be used with all projects. - Vertex Arrays support (Simulation with SDL 2).
When you want to use a texture with vertices, you must set the optional parameter **useWithVertices** to **true** in the **loadFromFile()** function, which is necessary when using SFML with SDL2. ```cpp sf::Texture texture; texture.loadFromFile("image.png", true); ``` ## Extras - Here is a **Web Game : Arkanoid** created thanks to the **[game engine tutorial](https://youtu.be/wo2-ofNB7Hw)**.
**It's a Web Game so it's playable on PC and mobile.** [![Image](https://i48.servimg.com/u/f48/20/16/75/27/arkano11.png)](https://is-daouda.github.io/) - The engine comes with a **[Demo (2D Platform Game)](https://github.com/Is-Daouda/is-Engine-Demo)** that uses only the functions of the engine, in order to show you its power and how to use it. Now imagine what you can do when you use Box 2D and the other tools! [![Image](https://i48.servimg.com/u/f48/20/16/75/27/demo_s15.png)](https://github.com/Is-Daouda/is-Engine-Demo) - [Example Pack](https://github.com/Is-Daouda/is-Engine-Example-Pack) that show how to use the various features of the game engine. ## Game Engine User Guide and Youtube Tutorial - [English version](./doc/isEngine_api_doc_eng.html) - [French version](./doc/isEngine_api_doc_fr.html) - [Youtube tutorial : How to import an SFML project into is::Engine](https://youtu.be/x_YQLHoPMbc) - [Youtube tutorial : How to create a game (Arkanoid) with the game engine](https://youtu.be/wo2-ofNB7Hw) ## Example of a project created with the engine - [I Can Transform](https://www.gamepix.com/play/i-can-transform) - [GravytX The Gravytoid](https://www.gamepix.com/play/Gravytx-the-gravytoid) ## Hello Scene Example Here is an example code of a Scene (the place where the game objects come to life). **In less than 50 lines of code, the engine allows to:** - Change the language of the game (English / French) - Display an RPG type message with interaction - Animate an **[object](./app/src/main/cpp/app_src/objects/HelloWorld.h)** with the possibility of interacting with it (by clicking / touching it) - Load resources (Textures, Music) - Play music - Display a background that scrolls vertically and horizontally with a speed - Manages the display depth of each object - Automatically manages the game window (closing confirmation, events, ...) - Change the background color of the window (scene color) ```cpp class HelloScene : public is::GameDisplay { public: HelloScene(is::GameSystemExtended &gameSysExt): GameDisplay(gameSysExt, sf::Color::White /* => scene color*/) {} void loadResources() { m_gameSysExt.m_gameLanguage = is::lang::ENGLISH; // set default game language // uncomment to change English language in French // m_gameSysExt.m_gameLanguage = is::lang::FRANCAIS; // load font and texture GameDisplay::loadParentResources(); // allows to load system resource (very important never forgot to call him) GRMaddTexture("hello_world", is::GameConfig::SPRITES_DIR + "hello_world.png"); auto &texBg = GRMaddTexture("background", is::GameConfig::TILES_DIR + "background.png"); auto &texDialog = GRMaddTexture("dialog_box", is::GameConfig::GUI_DIR + "dialog_box.png"); // add a background to the position x = 0, y = 0 which will fill the scene and which will be scrolled (scrolling speed = 0.5) SDMaddSceneObject(std::make_shared(texBg, 0.f, 0.f, this, 0.5f, -0.5f, false, false)); // add an object at position x = 0, y = 0 which will be updated and displayed in the scene SDMaddSceneObject(std::make_shared(0.f, 0.f, this)); // add RPG style game dialog auto gameDialog = std::make_shared(texDialog, GRMgetFont("font_msg"), this); gameDialog->setDepth(-2); // the display depth (make it appear on all objects. The object with the smallest value appears on the others) gameDialog->setDialog(is::GameDialog::DialogIndex::DIALOG_HELLO); // set the corresponding dialog (See GameDialog.h and GameLanguage.h for more details on creating a message for dialogue) SDMaddSceneObject(gameDialog); GSMaddSound("game_music", is::GameConfig::MUSIC_DIR + "game_music.wav"); // add music GSMplaySound("game_music"); // play music } }; ``` ## Prerequisites - [SFML Library (2.4 +)](https://www.sfml-dev.org/download.php) - [SDL 2 (2.0.12 +)]() - GCC Compiler (7.3 +) --- ## How to use is::Engine with the different development tools: ## ![switch](https://i48.servimg.com/u/f48/20/16/75/27/switch10.png) Nintendo Switch This project uses the template of **[carstene1ns](https://github.com/carstene1ns/switch-sdl2-demo)**. **1. Prerequisites** - DevkitPro with MSYS2 ## ![danger](https://i48.servimg.com/u/f48/20/16/75/27/icon_d10.png) Very important - Not affiliated with Nintendo. - All your source files (only .cpp or .c) must be located in the root of the **[cpp](./app/src/main/cpp/)** folder otherwise the compiler will not find them! - The Switch uses the same touch functions as Android. - The engine has been configured so that you can use the PC functions **keyIsPressed(is::GameConfig::KEY_UP)** or **keyIsPressed(is::GameConfig::KEY_A), etc.** on the Switch. - Some SFML functions like: **Render Texture** are not yet supported. These additions will be made soon! **2. Installation** ##### Windows 1. Download [devkitPro](https://github.com/devkitPro/installer/releases/download/v3.0.3/devkitProUpdater-3.0.3.exe) and install it in **C:/devkitPro/**. 2. During installation: - Check "devkitA64" (for Switch) - Check "MSYS2 Base System" - Check "portlibs" if available 3. Once installed, open the MSYS2 terminal (C:\devkitPro\msys2\msys2.exe) 4. Enter these commands in the console to download libraries: ```bash pacman -Syu pacman -S switch-sdl2 switch-sdl2_image switch-sdl2_mixer switch-sdl2_net switch-sdl2_ttf ``` 6. Move the **is-Engine** project to your **C:/ (C:/is-Engine)**. 7. Run the file **[copy_assets.cmd](./app/src/main/copy_assets.cmd)** so that it transfers your resources which are in the **[assets](./app/src/main/assets) folder** to romfs (this folder will be created when executing the copy_assets.cmd file) necessary for the compilation. 8. Enter these commands in the console: ```bash cd c:/is-Engine/app/src/main/ make ``` If all goes well you will have a **main.nro file** in the [main](./app/src/main/) folder that you can launch via Nintendo Switch emulators (Yuzu, Ryujinx, ...). ![Image](./images/demo_screen.png) **Enjoy!** --- ## ![android](https://i48.servimg.com/u/f48/20/16/75/27/icon_a10.png) Android Studio This project uses the template of **[Georgik](https://github.com/georgik/sdl2-android-example)** and **[Lauchmelder23](https://github.com/Lauchmelder23/SDLU)**. **1. Prerequisites** - Android Studio (4.0.1 +) - Android SDK and NDK (r20b) ## ![danger](https://i48.servimg.com/u/f48/20/16/75/27/icon_d10.png) Very important - On Android SFML games run with SDL library. If you want to use SDL functions in your source code, use the **IS_ENGINE_SDL_2 macro**. - The audio format supported at the moment is **.WAV** - Some SFML functions like: **Render Texture** are not yet supported. These additions will be made soon! - **Your help to improve the engine will be welcome!** - [Please read this](#Contribute). **2. Installation** ##### Windows 1. Download [Android Studio 3.x](https://developer.android.com/studio) (recommended version 4.0.1). 2. Download the [Android SDK](https://developer.android.com/studio) and install it in **C:/Android/SDK**. 3. Download [Android NDK android-ndk-r20b-windows-x86_64](https://developer.android.com/ndk/downloads/older_releases.html) and create a folder on your disk as follows **C:/Android/NDK** then extract the contents of the zip in this folder. 4. Set the environment variable **ANDROID_NDK** with the path **C:/Android/NDK**. 6. Move the **is-Engine** project to your **C:/ (C:/is-Engine)**. 7. Open the **is-Engine** folder with **Android Studio** and start the compilation. If all goes well you will have a **Hello World Screen** on your **Android emulator**. ![Image](./images/demo_screen.png) **Enjoy!** **2. How to replace the package name (com.author.isengine) of the application** - Follow these steps carefully. A single error and the application will crash wonderfully when launching on emulator / mobile! 1. Replace this line in the [gradle.app](./app/build.gradle#L32) file. 2. Replace this line in the [AndroidManifest.xml](./app/src/main/AndroidManifest.xml#L3) file. 3. Replace this line in the [SDLActivity.java](./app/src/main/java/com/author/isengine/SDLActivity.java#L1) file. 4. Replace the abresence **[com/author/isengine](./app/src/main/java/com/author/isengine/)** in which is the file [SDLActivity.java](./app/src/main/java/com/author/isengine/SDLActivity.java#L1) that you have just modified at the top by yours (example **com/yourname/yourgamename**). 5. Replace this part **..._ com_author_isengine _...** of line [20](./app/src/main/cpp/SDL_android_main.c#L20) and [23](./app/src/main/cpp/SDL_android_main.c#L23) in the file [SDL_android_main.c](./app/src/main/cpp/SDL_android_main.c#L20) by yours (example **com_yourname_yourgamename)**. 6. Replace this part **..._ com_author_isengine _...** on the 23 lines of the file [SDL_android.c](./libs/SDL2/src/core/android/SDL_android.c#L156) by yours (example **com_yourname_yourgamename**). - **I strongly advise you to use the replace function of your text editor** (on Notepad++ we use Ctrl + F + Replace option). 7. Replace this line in the [GameConfig.h](./app/src/main/cpp/app_src/config/GameConfig.h#L148) file. - Note that this part is only required if you want to use the game engine data save / load functions. **3. Adding Source Files** - So that Android Studio can detect your source files (.cpp) you must include them in the **[app_src.cmake](./app/src/main/cmake/app_src.cmake) or [isengine.cmake](./app/src/main/cmake/isengine.cmake)** file which is located in the **[is-Engine/app/src/main/cmake](./app/src/main/cmake/)** location. **4. Application location** - The application can be found in **is-Engine/app/build/outputs/apk**. --- ## ![web](https://i48.servimg.com/u/f48/20/16/75/27/icon_w10.png) Web (HTML 5 - CSS 3) If you want to make your SFML project compatible with the Web (Be able to run it in a web browser), please watch this **[video tutorial](https://youtu.be/x_YQLHoPMbc)**.
![danger](https://i48.servimg.com/u/f48/20/16/75/27/icon_d10.png) Now you can put texts and geometric shapes (Rectangle, Circle) in Outline on the Web **(It was not available in the old versions)**! **1. Prerequisites** - Emscripen (1.39.7 +) - Python (3.8.1 +) - CMake (3.1 +) - Java - SDL 2 **(It is downloaded with the internet connection when executing commands)** **2. Installation** ##### Windows 1. Download [Emscripten](https://github.com/emscripten-core/emsdk) and install it in **C:/emsdk**, define its path in the environment variable **Path** 2. Download [Python](https://www.python.org/downloads/release/python-381/) after installation, define its path in the environment variable **Path** 3. Download [CMake](https://cmake.org/download/) after installation, define its path in the environment variable **Path** 4. Download [Java](https://www.oracle.com/java/technologies/javase-jre8-downloads.html) after installation, define its path in the environment variable **Path** 5. Move the **is-Engine** project to your **C:/ (C:/is-Engine)**. 6. Execute this command : ```bash cd c:/is-Engine/app/src/main mkdir bin-web cd bin-web emsdk activate latest emcmake cmake .. make -j3 python -m http.server ``` 7. Visit this url **localhost:8000** in your **Web Browser**. If all goes well you will have a **Hello World Screen** on your **Web Browser**. ![Image](./images/demo_screen.png) **Enjoy!** **3. Adding Source Files** - In order for CMake to detect your source files (.cpp) you must include them in the **[app_src.cmake](./app/src/main/cmake/app_src.cmake) or [isengine.cmake](./app/src/main/cmake/isengine.cmake)** file which is located in the **[is-Engine/app/src/main/cmake](./app/src/main/cmake/)** location. ## ![danger](https://i48.servimg.com/u/f48/20/16/75/27/icon_d10.png) Very important - **is::Engine** works on the Web thanks to **SDL 2**. - These libraries: **TMXLite, TMXLoader, TinyFileDialog** are not supported in the web version of is::Engine. - If you want to use SDL functions in your source code, use the **IS_ENGINE_SDL_2 macro**. - Note that some SFML functions like: **Render Texture** are not yet supported. These additions will be made soon! --- ## ![Web Push Notification](https://i48.servimg.com/u/f48/20/16/75/27/notif_10.png) Web Push Notification - If you want to make your SFML project compatible with the Web (Be able to run it in a web browser), please watch this **[video tutorial](https://youtu.be/x_YQLHoPMbc)**. - ![danger](https://i48.servimg.com/u/f48/20/16/75/27/icon_d10.png)
By default the web push notification has been disabled. Because to make it work you must have an internet connection. In case there is no internet access and it is not well launched, it can prevent the execution of the web program.
To enable it, please modify these lines in **[index.html](./app/src/main/web/index.html)** : **[7](./app/src/main/web/index.html#L7), [23](./app/src/main/web/index.html#L23), [108](./app/src/main/web/index.html#L108)** #### Installation - This shows how to test the push notification. Note that normally to use it, you have to associate it with a database (backend). But here we will use it with the **Push Companion** site **(It will serve as a backend for us!)**. - For more information on Push Notification please see this [page](https://developers.google.com/web/fundamentals/codelabs/push-notifications). 1. Web browser ([preferably Google Chrome](https://www.google.fr/chrome/?brand=CHBD&brand=XXVF&gclid=EAIaIQobChMI7a315b6c7gIVEKSyCh0O8QJjEAAYASABEgJfd_D_BwE&gclsrc=aw.ds)) 2. [Web server for Chrome](https://chrome.google.com/webstore/detail/web-server-for-chrome/ofhbbkphhbklhfoeikjpcbhemlocgigb) or your own web server. 3. Define the **Application Server Keys** in the [main.js](./app/src/main/web/scripts/main.js#L25) file. You can get this keys (We will use the public key) [here](https://web-push-codelab.glitch.me/). 4. Launch the **[web](./app/src/main/web)** or **bin-web folder (generate using emscripten)** via the web server. 5. Click on the **"Enable Push Messaging" button** to activate the sending of Push Notifications. Once done you will have a code (which can be used to send you push notifications). 6. Go to this [site](https://web-push-codelab.glitch.me/) and enter the code in the **"Subscription to Send To" text field** followed by your message in **"Text to Send" text field**. Click on the **"Send Push Message" button**. 7. If all goes well you will have a push notification followed by the message you sent in the console (development tool) of your browser. ![image](https://i.servimg.com/u/f48/20/16/75/27/image10.jpg) #### Configure the Push Notification 1. To change the **information (title, details, ...)** of the Push Notification you must refer to the [sw.js](./app/src/main/web/sw.js#L28) file. 2. To change the **Push Notification image** files, refer to the [images](./app/src/main/web/images) folder. 3. To change the **page that is launched** when you click on the notification, refer to the [sw.js](./app/src/main/web/sw.js#L45) file. --- ## ![cmake](https://i48.servimg.com/u/f48/20/16/75/27/icon_c11.png) CMake **1. Prerequisites** - CMake (3.1 +) **2. Installation** #### Windows 1. Compile SFML with CMake to have **static libraries** and put on **C:/ (C:/SFML)**. 2. Move the **is-Engine** project to your **C:/ (C:/is-Engine)**. 3. Execute this command : ```bash cmake -S "C:/is-Engine" -B "C:/build" cd build make ``` #### Linux 1. Install SFML 2.5.1 on your machine. 2. Move the **is-Engine** project to **/home/user/ (/home/user/is-Engine)**. 3. Execute this command : ```bash sudo cmake -S "is-Engine" -B "build" cd build sudo make ``` **3. After installation** - You will have a **bin** folder in which the engine demo is located. **5. Adding Source Files** - In order for CMake to detect your source files (.cpp) you must include them in the **[app_src.cmake](./app/src/main/cmake/app_src.cmake) or [isengine.cmake](./app/src/main/cmake/isengine.cmake)** file which is located in the **[is-Engine/app/src/main/cmake](./app/src/main/cmake/)** location. --- ## ![vs](https://i48.servimg.com/u/f48/20/16/75/27/vs_ima11.png) Visual Studio **1. Installation** #### Windows Download Visual Studio Community 2022 [here](https://visualstudio.microsoft.com/fr/vs/community/) and install it. **2. Opening the project with the IDE:** #### Windows Open the file **vs-sfml.sln** in the location **[is-Engine/app/src/main/](./app/src/main/)** --- ## ![vssdl](https://i48.servimg.com/u/f48/20/16/75/27/vs_ima11.png) Visual Studio SDL 2 **1. Installation** #### Windows 1. Download Visual Studio Community 2022 [here](https://visualstudio.microsoft.com/fr/vs/community/) and install it. 2. Download this [file](https://github.com/GlowCheese/SDL2-Setup/releases/download/v1.1.0/SDL2.Compiler.zip) and extract it to C:\ (**the location of the folder must be C:\SDL2-2.26.3. Otherwise it will not work.**). **2. Opening the project with the IDE:** #### Windows Open the file **SDL2_SFML.sln** in the location **[is-Engine/app/src/main/](./app/src/main/)** --- ## ![qt](https://i48.servimg.com/u/f48/20/16/75/27/qt_ico10.png) Qt **1. Installation** #### Windows 1. Download Qt 5.12.9 MinGW [here](http://qtproject.mirror.liquidtelecom.com/archive/qt/5.12/5.12.9/qt-opensource-windows-x86-5.12.9.exe) and install it. 2. Download this [version of SFML](https://github.com/Is-Daouda/SFML_Qt_MinGW) already compiled for Qt 5.12.9 and extract it in **C:/ (C:/SFML_Qt_MinGW)**. **2. Opening the project with the IDE:** #### Windows 1. Run the file **open_qt_creator.bat** in the main directory *(Make sure you have included the path to the Qt executable in your PATH environment variable)*. 2. Or open the file **is-Engine.pro** in the location **[is-Engine/app/src/main/qt](./app/src/main/qt/)** **3. Executable location** - The compiler files can be found in **is-Engine/app/src/main/bin-Qt**. --- ## ![vsc](https://i48.servimg.com/u/f48/20/16/75/27/icon_v10.png) Visual Studio Code This project uses the template of **andrew-r-king**. For more information on this template [click here](https://github.com/andrew-r-king/sfml-vscode-boilerplate). **1. Prerequisites** #### Windows - [SFML 2.5.1 - GCC 7.3.0 MinGW (DW2) 32-bit](https://www.sfml-dev.org/files/SFML-2.5.1-windows-gcc-7.3.0-mingw-32-bit.zip) - [GCC 7.3.0 MinGW (DW2) 32-bit](https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/7.3.0/threads-posix/dwarf/i686-7.3.0-release-posix-dwarf-rt_v5-rev0.7z/download) - [Git Bash](https://git-scm.com/downloads) #### Linux - Get SFML 2.5.1 from your distro if it has it, or compile from source. **2. Installation** #### Windows 1. Download & Extract SFML 2.5.1 to **C:/SFML/** where the bin/lib/include folders are contained within. 2. Download & Extract MinGW to **C:/mingw32/** where the bin/lib/include folders are contained within. #### Linux 1. Ensure the GCC Toolchain is installed (**sudo apt install build-essential**). 2. Run **sudo apt install libsfml-dev**. The SFML version you got will vary depending on the distro. 2.5.1 is included in [Ubuntu 19.04 Disco Dingo](http://cdimage.ubuntu.com/daily-live/current/HEADER.html) for example. **3. Opening the project with the IDE:** - Rename the **Makefile-vscode** file to **Makefile** in **[is-Engine/app/src/main/](./app/src/main/)**. #### Windows 1. Run the file **open_vscode.bat** in the main directory. #### Linux 2. Execute this command in the main directory: ```bash code -n "./app/src/main" ``` #### All 3. Or open the **[is-Engine/app/src/main](./app/src/main/)** folder with **Solution Explorer**. **4. Executable location** - The compiler files can be found in **is-Engine/app/src/main/bin-vscode**. --- ## ![cb](https://i48.servimg.com/u/f48/20/16/75/27/icon_c10.png) Code::Blocks **1. Installation** #### Windows 1. Download Code::Blocks 20.03 MinGW [here](https://sourceforge.net/projects/codeblocks/files/Binaries/20.03/Windows/codeblocks-20.03mingw-setup.exe/download) and install it. 2. Download this [version of SFML](https://github.com/Is-Daouda/SFML_CB_MinGW) already compiled for Code::Blocks 20.03 and extract it in **C:/ (C:/SFML_CB_MinGW)**. #### Linux 1. Download Code::Blocks 20.03 and install it. 2. Ensure the GCC Toolchain is installed (**sudo apt install build-essential**). 3. Run **sudo apt install libsfml-dev**. The SFML version you got will vary depending on the distro. 2.5.1 is included in [Ubuntu 19.04 Disco Dingo](http://cdimage.ubuntu.com/daily-live/current/HEADER.html) for example. **2. Opening the project with the IDE:** #### Windows 1. Run the file **open_codeblocks.bat** in the main directory *(Make sure you have included the path to the Code::Blocks executable in your PATH environment variable)*. 2. Or open the file **is-Engine-windows.cbp** in the location **[is-Engine/app/src/main](./app/src/main/)** #### Linux 1. Execute this command in the main directory: ```bash codeblocks "./app/src/main/is-Engine-linux.cbp" ``` 2. Or open the file **is-Engine-linux.cbp** in the location **[is-Engine/app/src/main](./app/src/main/)**. **3. Executable location** - The compiler files can be found in **is-Engine/app/src/main/bin-codeblocks**. --- ## ![cb](https://i48.servimg.com/u/f48/20/16/75/27/icon_c10.png) Develop SFML games with SDL 2 **1. Installation** #### Windows 1. Download Code::Blocks 20.03 MinGW [here](https://sourceforge.net/projects/codeblocks/files/Binaries/20.03/Windows/codeblocks-20.03mingw-setup.exe/download) and install it. 2. Download this [version of SDL 2](https://github.com/Is-Daouda/SDL2) and extract it in **C:/ (C:/SDL2)**. 3. Put the **.dll files** which is in the **bin** folder of SDL2 in the **[main](./app/src/main/)** folder. #### Linux 1. Download Code::Blocks 20.03 and install it. 2. Ensure the GCC Toolchain is installed (**sudo apt install build-essential**). 3. Run **sudo apt install libsdl2-2.0-0 libsdl2-gfx-1.0-0 libsdl2-image-2.0-0 libsdl2-mixer-2.0-0 libsdl2-net-2.0-0 libsdl2-ttf-2.0-0** to install all SDL 2 libraries. **2. Opening the project with the IDE:** #### Windows 1. Run the file **open_codeblocks_sdl.bat** in the main directory *(Make sure you have included the path to the Code::Blocks executable in your PATH environment variable)*. 2. Or open the file **is-Engine-windows-SDL2.cbp** in the location **[is-Engine/app/src/main](./app/src/main/)** #### Linux 1. Execute this command in the main directory: ```bash codeblocks "./app/src/main/is-Engine-linux-SDL2.cbp" ``` 2. Or open the file **is-Engine-linux-SDL2.cbp** in the location **[is-Engine/app/src/main](./app/src/main/)**. **3. Executable location** - The compiler files can be found in **is-Engine/app/src/main/bin-codeblocks**. ## ![danger](https://i48.servimg.com/u/f48/20/16/75/27/icon_d10.png) Very important - If you want to use SDL functions in your source code, use the **IS_ENGINE_SDL_2 macro**. - Note that some SFML functions like: **Render Texture** are not yet supported. These additions will be made soon! --- ## ![icon](https://i48.servimg.com/u/f48/20/16/75/27/icon10.png) Change application icon: #### Nintendo Switch - To change the icon of the application you must go to the location **[is-Engine/app/src/main/](./app/src/main/)**. #### Android - To change the icon of the application you must go to the location **[is-Engine/app/src/main/res](./app/src/main/res/)** replace all the images (PNG) which are in the **drawable** subfolders. #### Web (HTML 5 - CSS 3) - To change the icon of the application you must go to the location **[is-Engine/app/src/main/web](./app/src/main/web/)**. #### Windows - To change the icon of the application you must go to the location **[is-Engine/app/src/main/env/windows](./app/src/main/env/windows)** replace all the images **(Attention CMake uses the same resources).** #### Linux - To change the icon of the application you must go to the location **[is-Engine/app/src/main/env/linux](./app/src/main/env/linux)**. --- ## Description of the project structure: ![header](./images/is_Engine_structure.png) ---------------------------- - The source files of the project can be found in the **[is-Engine/app/src/main/cpp](./app/src/main/cpp/)** location. #### 1. [main.cpp](./app/src/main/cpp/main.cpp) file Contains the entry point of the program, inside there are two instructions : - `game.play()`: Launches the engine rendering loop which allows to manage the introduction screen, main menu, level and game over. - `game.basicSFMLmain()` (disabled by default): Launches the display of a classic SFML window. The implementation is in the **[basicSFMLmain.cpp](./app/src/main/cpp/basicSFMLmain.cpp)** file. *Very useful if you already have a project under development and you want to associate it with the engine. You can also use it to implement your own components to the engine.* ---------------------------- #### 2. [app_src](./app/src/main/cpp/app_src/) folder Contains the source code of the game. Description of these sub-directories: - **[activity](./app/src/main/cpp/app_src/activity/)** : Contains the **[Activity](./app/src/main/cpp/app_src/activity/GameActivity.h)** class which allows the interaction of the different scenes of the game. - **[config](./app/src/main/cpp/app_src/config/)** : Contains the **[GameConfig.h](./app/src/main/cpp/app_src/config/GameConfig.h)** file which allows to define the general parameters of the game. It also contains the file **[ExtraConfig.h](./app/src/main/cpp/app_src/config/ExtraConfig.h)** which allows to activate / deactivate certain engine functionality (Engine optimization, SDM, Admob, Main Render Loop, ...). - **[gamesystem_ext](./app/src/main/cpp/app_src/gamesystem_ext/)** : Contains **[GameSystemExtended](./app/src/main/cpp/app_src/gamesystem_ext/GameSystemExtended.h)** a class derived from **[GameSystem](./app/src/main/cpp/isEngine/system/function/GameSystem.h)** which allows to manipulate game data (save, load, ...). - **[language](./app/src/main/cpp/app_src/language/)** : Contains the **[GameLanguage.h](./app/src/main/cpp/app_src/language/GameLanguage.h)** file which allows to manage everything related to game languages. - **[levels](./app/src/main/cpp/app_src/levels/)** : Contains game levels and the **[Level.h](./app/src/main/cpp/app_src/levels/Level.h)** file which allows to integrate them into the game. - **[objects](./app/src/main/cpp/app_src/objects/)** : Contains the objects that will be used in the different scenes. - **[scenes](./app/src/main/cpp/app_src/scenes/)** : Contains the different scenes of the game (Introduction, Main menu, ...). ---------------------------- #### 3. [assets](./app/src/main/assets/) folder Contains game resource files (music, sound sfx, image, ...) ---------------------------- #### 4. [isEngine](./app/src/main/cpp/isEngine/) folder Contains the source code of the game engine --- ## Special things to know about the engine ### is::LibConnect With the is::LibConnect you can write code for a specific library. Here is how to do it: ```cpp sf::Text text; text.setString( // on PC (Windows / Linux) #if define(IS_ENGINE_SFML) "We use SFML 2 library" // When we develop for the Nintendo Switch #elif define(IS_ENGINE_SWITCH) "SFML 2 on Switch" // on Android or when you use SDL to create SFML games on PC (only for Code::Block at the moment) #elif define(IS_ENGINE_SDL_2) "Run SFML 2 with SDL 2" // When we develop for the web (HTML 5) with Emscripten #elif define(IS_ENGINE_HTML_5) "SFML 2 on Web" #endif ); ``` #### If you have discovered another way to use the game engine, don't hesitate to share it! We will put it in this Special section so that other people can benefit from it! --- ## How to update an is::Engine project 1. First of all the part of is::Engine that changes most often during updates is the [isEngine](./app/src/main/cpp/isEngine/) folder. But it also happens that these files can be modified: - [GameActivity.h](./app/src/main/cpp/app_src/activity/GameActivity.h) - [GameConfig.h](./app/src/main/cpp/app_src/language/GameLanguage.h) - [ExtraConfig.h](./app/src/main/cpp/app_src/config/ExtraConfig.h) - [GameSystemExtended.h](./app/src/main/cpp/app_src/gamesystem_ext/GameSystemExtended.h) - [basicSFMLmain.cpp](./app/src/main/cpp/basicSFMLmain.cpp) - [GameLanguage.h](./app/src/main/cpp/app_src/language/GameLanguage.h) - And the files which is in [cmake](./app/src/main/cmake) and [web](./app/src/main/web) folder. - ![danger](https://i48.servimg.com/u/f48/20/16/75/27/icon_d10.png) **So watch them carefully in case you encounter any errors during migration!** 2. To update your old project with a new version of is::Engine: the files (.h and .cpp) you need to move are in [objects](./app/src/main/cpp/app_src/objects/) and [scenes](./app/src/main/cpp/app_src/scenes/). **Note that these folders never change whatever the version!** --- ## How to activate the use of Admob? Coming soon! --- ## Contribute - If you want to participate in the development of the project to help me improve the engine, please note that you are welcome! Together we go further! - One of the objectives of this project is to create a large community that can work on the engine to allow many people around the world to easily realize their dream games / applications! ## Contacts * For any help please contact me on my [email address](mailto:isdaouda.n@gmail.com) * You can follow me on Twitter for more informations on my activities [@Is Daouda Games](https://twitter.com/IsDaouda_Games) ================================================ FILE: app/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.4.1) include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../libs/SDL2/include # SDL_internal.h is located in src required by SDL_android_main.c bridge ${CMAKE_CURRENT_SOURCE_DIR}/../libs/SDL2/src ${CMAKE_CURRENT_SOURCE_DIR}/../libs/SDL2_image/include ${CMAKE_CURRENT_SOURCE_DIR}/../libs/SDL2_gfx/include ${CMAKE_CURRENT_SOURCE_DIR}/../libs/SDL2_mixer/include ${CMAKE_CURRENT_SOURCE_DIR}/../libs/SDL2_ttf/include ) set(ISENGINE_ANDROID true) # This confirms that we are using is::Engine to develop on Android set(ISENGINE_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp") set(ISENGINE_CMAKE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src/main/cmake") add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../libs/SDL2/ ${CMAKE_CURRENT_SOURCE_DIR}/../libs/SDL2) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../libs/SDL2_image/ ${CMAKE_CURRENT_SOURCE_DIR}/../libs/SDL2_image) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../libs/SDL2_gfx/ ${CMAKE_CURRENT_SOURCE_DIR}/../libs/SDL2_gfx) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../libs/SDL2_mixer/ ${CMAKE_CURRENT_SOURCE_DIR}/../libs/SDL2_mixer) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../libs/SDL2_ttf/ ${CMAKE_CURRENT_SOURCE_DIR}/../libs/SDL2_ttf) #include_directories(${FIREBASE_INCLUDE_DIR}) #link_directories("${FIREBASE_LIBRARY_DIR}${ANDROID_ABI}/c++/") include(${ISENGINE_CMAKE_DIR}/isengine.cmake) include(${ISENGINE_CMAKE_DIR}/app_src.cmake) add_library( isengine SHARED # Provides a relative path to your source file(s). ${isengine} ${app_src} ) find_library( # Sets the name of the path variable. log-lib # Specifies the name of the NDK library that # you want CMake to locate. log ) target_link_libraries( # Specifies the target library. isengine SDL2 SDL2_image SDL2_gfx SDL2_mixer SDL2_ttf #admob #app ${log-lib} ) ================================================ FILE: app/build.gradle ================================================ buildscript { repositories { google() mavenLocal() maven {url 'https://maven.google.com'} jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.4.2' //classpath 'com.google.gms:google-services:4.0.1' } } apply plugin: 'com.android.application' /* def ndkDir = System.getenv("ANDROID_NDK") def propertiesFile = project.rootProject.file('local.properties') if (propertiesFile.exists()) { Properties properties = new Properties() properties.load(propertiesFile.newDataInputStream()) ndkDir = properties.getProperty('ndk.dir') } */ android { compileSdkVersion 30 buildToolsVersion '28.0.3' defaultConfig { applicationId = 'com.author.isengine' //Replace this with your real package name (e.g. com.StudioName.GameName) minSdkVersion 19 targetSdkVersion 30 versionCode 1 versionName "1.0" //multiDexEnabled true externalNativeBuild { cmake { cppFlags "-std=c++1z -std=c++14 -std=c++17 -frtti -fexceptions" arguments "-DANDROID_STL=c++_shared", "-DANDROID_TOOLCHAIN=clang" //, //"-DFIREBASE_INCLUDE_DIR=${ndkDir}/sources/firebase_cpp_sdk/include", //"-DFIREBASE_LIBRARY_DIR=${ndkDir}/sources/firebase_cpp_sdk/libs/android/" abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86_64' } } } buildTypes { debug { minifyEnabled false jniDebuggable true debuggable false } release { minifyEnabled = false proguardFiles.add(file('proguard-rules.txt')) //proguardFile file("${ndkDir}/sources/firebase_cpp_sdk/libs/android/app.pro") //proguardFile file("${ndkDir}/sources/firebase_cpp_sdk/libs/android/admob.pro") } } externalNativeBuild { cmake { path "CMakeLists.txt" version "3.10.2" } } /*sourceSets { main { // let gradle pack the shared library into apk jniLibs.srcDirs = [ "${ndkDir}/sources/firebase_cpp_sdk/libs/android/" ] } }*/ } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) //implementation 'com.android.support:multidex:1.0.3' } ================================================ FILE: app/src/main/.vs/vs-sfml/v17/DocumentLayout.json ================================================ { "Version": 1, "WorkspaceRootPath": "C:\\is-Engine\\app\\src\\main\\", "Documents": [], "DocumentGroupContainers": [ { "Orientation": 0, "VerticalTabListWidth": 256, "DocumentGroups": [] } ] } ================================================ FILE: app/src/main/.vs/vs-sfml/v17/ipch/AutoPCH/ee33401603bb6f9/HELLOSCENE.ipch ================================================ [File too large to display: 72.1 MB] ================================================ FILE: app/src/main/.vs/vs-sfml/v17/ipch/AutoPCH/f64c3c32af64e908/BASICSFMLMAIN.ipch ================================================ [File too large to display: 77.6 MB] ================================================ FILE: app/src/main/.vscode/_keybindings.json ================================================ // Place your key bindings in this file to overwrite the defaults [ { "key": "f8", "command": "-editor.action.marker.next", "when": "editorFocus && !editorReadonly" }, { "key": "f9", "command": "-editor.debug.action.toggleBreakpoint", "when": "editorTextFocus" }, { "key": "f10", "command": "-workbench.action.debug.stepOver", "when": "inDebugMode" }, { "key": "shift+f8", "command": "-editor.action.marker.prev", "when": "editorFocus && !editorReadonly" }, { "key": "shift+f9", "command": "-editor.debug.action.toggleInlineBreakpoint", "when": "editorTextFocus" }, { "key": "shift+f10", "command": "-editor.action.showContextMenu", "when": "editorTextFocus" }, { "key": "f8", "command": "workbench.action.debug.selectandstart" }, { "key": "f9", "command": "workbench.action.tasks.runTask", "args": "Build & Run: Release" }, { "key": "shift+f9", "command": "workbench.action.tasks.runTask", "args": "Run: Release" }, { "key": "f10", "command": "workbench.action.tasks.runTask", "args": "Build & Run: Debug" }, { "key": "shift+f10", "command": "workbench.action.tasks.runTask", "args": "Run: Debug" }, ] ================================================ FILE: app/src/main/.vscode/c_cpp_properties.json ================================================ { "configurations": [ { "name": "Linux", "intelliSenseMode": "gcc-x64", "includePath": [ "${workspaceFolder}/cpp", "${workspaceFolder}/lib", "${workspaceFolder}/test", "~/SFML/include", "/usr/local/include/**", "/usr/include/**" ], "defines": [ "_DEBUG" ], "cStandard": "c11", "cppStandard": "c++17", "forcedInclude": [ "${workspaceFolder}/cpp/PCH.hpp" ] }, { "name": "Mac", "intelliSenseMode": "clang-x64", "compilerPath": "/usr/bin/clang", "macFrameworkPath": [ "/Library/Frameworks", "/System/Library/Frameworks" ], "includePath": [ "${workspaceFolder}/cpp", "${workspaceFolder}/lib", "${workspaceFolder}/test", "/usr/local/include/**" ], "defines": [ "_DEBUG" ], "cStandard": "c11", "cppStandard": "c++17", "forcedInclude": [ "${workspaceFolder}/cpp/PCH.hpp" ] }, { "name": "Win32", "intelliSenseMode": "gcc-x64", "compilerPath": "C:/mingw32/bin/gcc.exe", "includePath": [ "${workspaceFolder}/cpp", "${workspaceFolder}/lib", "C:/SFML/include" ], "defines": [ "_DEBUG", "UNICODE", "_UNICODE" ], "cStandard": "c11", "cppStandard": "c++17", "forcedInclude": [ "${workspaceFolder}/cpp/PCH.hpp" ] } ], "version": 4 } ================================================ FILE: app/src/main/.vscode/launch.json ================================================ { "version": "0.2.0", "configurations": [ { "name": "GDB/LLDB", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/bin-vscode/Debug/${workspaceRootFolderName}.exe", "args": [ "-exec info registers" ], "stopAtEntry": false, "cwd": "${workspaceFolder}", "preLaunchTask": "Build: Debug", "externalConsole": false, "internalConsoleOptions": "neverOpen", "setupCommands": [ { "description": "Enable pretty-printing for gdb", "text": "-enable-pretty-printing", "ignoreFailures": true } ], "windows": { "MIMode": "gdb", "miDebuggerPath": "C:/mingw32/bin/gdb.exe", "env": { "Path": "${config:terminal.integrated.env.windows.Path}" } }, "linux": { "program": "${workspaceFolder}/bin-vscode/Debug/${workspaceRootFolderName}", "MIMode": "gdb", "miDebuggerPath": "/usr/bin/gdb", "env": { "PATH": "${config:terminal.integrated.env.linux.PATH}", } }, "osx": { "program": "${workspaceFolder}/bin-vscode/Debug/${workspaceRootFolderName}", "MIMode": "lldb", "env": { "PATH": "${config:terminal.integrated.env.osx.PATH}", } }, } ] } ================================================ FILE: app/src/main/.vscode/settings.json ================================================ { "C_Cpp.autocomplete": "Default", "C_Cpp.intelliSenseEngine": "Default", "C_Cpp.dimInactiveRegions": false, "C_Cpp.preferredPathSeparator": "Forward Slash", "C_Cpp.loggingLevel": "Warning", "C_Cpp.workspaceParsingPriority": "highest", "C_Cpp.enhancedColorization": "Disabled", "debug.toolBarLocation": "docked", "editor.fontFamily": "'Ubuntu Mono', 'Courier Prime Code', 'Courier', 'Courier New', Consolas, monospace", "editor.tabSize": 4, "editor.insertSpaces": false, "files.encoding": "utf8", "files.trimTrailingWhitespace": true, "files.eol": "\n", "files.exclude": { // hide file "res": true "save": true "CMakeLists.txt": true "AndroidManifest.xml": true "codeblocks": true "cmake": true "bin-codeblocks": true "bin-vscode": true "is-Engine-linux.cbp": true "is-Engine-linux.layout": true "is-Engine-linux.depend": true "is-Engine-windows.cbp": true "is-Engine-windows.layout": true "is-Engine-windows.depend": true ".vscode/launch.json": true, "env/windows/*.sh": true, "env/osx/dmg.applescript": true, "Makefile": true, "build.sh": true, "gmon.out": true, "bin/": true, "build/": true, "**/*.7z": true, "**/*.rar": true, "**/Thumbs.db": true }, "files.associations": { "*.json": "jsonc", "*.stats": "cpp", "*.desktop": "properties", ".clang-format": "yaml" }, "terminal.integrated.shell.windows": "C:/Program Files/Git/bin/bash.exe", "terminal.integrated.env.windows": { "Path": "C:/mingw32/bin;C:/SFML/bin" }, "terminal.integrated.env.linux": { "PATH": "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin" }, "terminal.integrated.env.osx": { "PATH": "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin" }, "workbench.colorCustomizations": { "statusBar.background": "#8ac242", "statusBar.foreground": "#ffffff", "statusBar.debuggingBackground": "#5e9517", "statusBar.debuggingForeground": "#ffffff", "terminal.ansiBrightRed": "#ee3355", "terminal.ansiBrightYellow": "#ffcc55", "terminal.ansiBrightGreen": "#a5ea4f", "terminal.ansiBrightBlue": "#5599ff" }, "[applescript]": { "files.encoding": "utf8" } } ================================================ FILE: app/src/main/.vscode/tasks.json ================================================ { // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "2.0.0", "windows": { "options": { "env": { "Path": "${config:terminal.integrated.env.windows.Path}" } } }, "linux": { "options": { "env": { "PATH": "${config:terminal.integrated.env.linux.PATH}" } } }, "osx": { "options": { "env": { "PATH": "${config:terminal.integrated.env.osx.PATH}" } } }, "presentation": { "echo": false, "reveal": "always", "focus": true, "panel": "shared", "clear": false, "showReuseMessage": true }, "tasks": [ { "label": "Build & Run: Release", "command": "bash ./build.sh buildrun Release vscode", "type": "shell", "group": { "kind": "build", "isDefault": true }, "problemMatcher": [ "$gcc" ], }, { "label": "Build: Release", "command": "bash ./build.sh build Release vscode", "type": "shell", "group": { "kind": "build", "isDefault": true }, "problemMatcher": [ "$gcc" ] }, { "label": "Run: Release", "command": "bash ./build.sh run Release vscode", "type": "shell", "group": { "kind": "build", "isDefault": true }, "problemMatcher": [ "$gcc" ] }, { "label": "Rebuild: Release", "command": "bash ./build.sh rebuild Release vscode", "type": "shell", "group": { "kind": "build", "isDefault": true }, "problemMatcher": [ "$gcc" ] }, { "label": "Build & Run: Debug", "command": "bash ./build.sh buildrun Debug vscode", "type": "shell", "group": { "kind": "build", "isDefault": true }, "problemMatcher": [ "$gcc" ] }, { "label": "Build: Debug", "command": "bash ./build.sh build Debug vscode", "type": "shell", "group": { "kind": "build", "isDefault": true }, "problemMatcher": [ "$gcc" ] }, { "label": "Run: Debug", "command": "bash ./build.sh run Debug vscode", "type": "shell", "group": { "kind": "build", "isDefault": true }, "problemMatcher": [ "$gcc" ] }, { "label": "Rebuild: Debug", "command": "bash ./build.sh rebuild Debug vscode", "type": "shell", "group": { "kind": "build", "isDefault": true }, "problemMatcher": [ "$gcc" ] }, { "label": "Profile: Debug", "command": "bash ./build.sh profile Debug vscode", "type": "shell", "group": { "kind": "build", "isDefault": true }, "problemMatcher": [ "$gcc" ] }, { "label": "Build & Run: Tests", "command": "bash ./build.sh buildrun Tests vscode '-w NoTests -s'", "type": "shell", "group": { "kind": "build", "isDefault": true }, "problemMatcher": [ "$gcc" ] }, { "label": "Build: Tests", "command": "bash ./build.sh build Tests vscode", "type": "shell", "group": { "kind": "build", "isDefault": true }, "problemMatcher": [ "$gcc" ] }, { "label": "Run: Tests", "command": "bash ./build.sh run Tests vscode '-w NoTests -s'", "type": "shell", "group": { "kind": "build", "isDefault": true }, "problemMatcher": [ "$gcc" ] }, { "label": "Rebuild: Tests", "command": "bash ./build.sh rebuild Tests vscode", "type": "shell", "group": { "kind": "build", "isDefault": true }, "problemMatcher": [ "$gcc" ] }, { "label": "Build: Production", "command": "bash ./build.sh buildprod Release vscode", "type": "shell", "group": { "kind": "build", "isDefault": true }, "problemMatcher": [ "$gcc" ] } ] } ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.15) project(isengine) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") if( ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") set(USE_FLAGS "-s USE_SDL=2 -s USE_SDL_IMAGE=2 -s SDL2_IMAGE_FORMATS=\"['png']\" -s -s USE_SDL_TTF=2 -s USE_SDL_MIXER=2 -s USE_OGG=1 -s USE_VORBIS=1 -s USE_FREETYPE=1") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${USE_FLAGS}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${USE_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${USE_FLAGS}") endif() include_directories(${CMAKE_SOURCE_DIR}/include ${SDL2_INCLUDE_DIRS} ${SDL2_IMAGE_INCLUDE_DIRS} ${SDL2_TTF_INCLUDE_DIRS} ${SDL2_MIXER_INCLUDE_DIRS} ${FREETYPE_INCLUDE_DIRS}) set(ISENGINE_HTML_5 true) # This confirms that we are using is::Engine to develop on the Web set(ISENGINE_SRC_DIR "cpp") set(ISENGINE_CMAKE_DIR "cmake") add_compile_definitions(IS_ENGINE_HTML_5) include(${ISENGINE_CMAKE_DIR}/app_src.cmake) include(${ISENGINE_CMAKE_DIR}/isengine.cmake) add_executable(isengine ${app_src} ${isengine} ) target_link_libraries(isengine ${SDL2_LIBRARIES} ${SDL2IMAGE_LIBRARIES} ${SDL2MIXER_LIBRARIES} ${SDL2TTF_LIBRARIES} ${FREETYPE_LIBRARIES} -lidbfs.js) set_property(TARGET isengine APPEND_STRING PROPERTY LINK_FLAGS " -s ALLOW_MEMORY_GROWTH=1") set_property(TARGET isengine APPEND_STRING PROPERTY LINK_FLAGS " -s DISABLE_EXCEPTION_CATCHING=2") set_property(TARGET isengine APPEND_STRING PROPERTY LINK_FLAGS " -s WASM=1") set_property(TARGET isengine APPEND_STRING PROPERTY LINK_FLAGS " -s TOTAL_MEMORY=134217728") # Release flags set_property(TARGET isengine APPEND_STRING PROPERTY LINK_FLAGS " --emrun") set_property(TARGET isengine APPEND_STRING PROPERTY LINK_FLAGS " -O3") set_property(TARGET isengine APPEND_STRING PROPERTY LINK_FLAGS " --bind") # Copy the web files configure_file(${CMAKE_CURRENT_SOURCE_DIR}/web/index.html ${CMAKE_CURRENT_BINARY_DIR}/index.html COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/web/sw.js ${CMAKE_CURRENT_BINARY_DIR}/sw.js COPYONLY) file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/web/images DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/web/scripts DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/web/styles DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) # Allow some files to be fetched. file(GLOB files "./assets/*" "./assets/*/*") foreach(file ${files}) file(RELATIVE_PATH relative_file ${CMAKE_SOURCE_DIR} ${file}) set_property(TARGET isengine APPEND_STRING PROPERTY LINK_FLAGS " --preload-file ${file}@/${relative_file}") endforeach() ================================================ FILE: app/src/main/Makefile ================================================ #--------------------------------------------------------------------------------- .SUFFIXES: #--------------------------------------------------------------------------------- ifeq ($(strip $(DEVKITPRO)),) $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") endif TOPDIR ?= $(CURDIR) include $(DEVKITPRO)/libnx/switch_rules #--------------------------------------------------------------------------------- # TARGET is the name of the output # BUILD is the directory where object files & intermediate files will be placed # SOURCES is a list of directories containing source code # DATA is a list of directories containing data files # INCLUDES is a list of directories containing header files # ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional) # # NO_ICON: if set to anything, do not use icon. # NO_NACP: if set to anything, no .nacp file is generated. # APP_TITLE is the name of the app stored in the .nacp file (Optional) # APP_AUTHOR is the author of the app stored in the .nacp file (Optional) # APP_VERSION is the version of the app stored in the .nacp file (Optional) # APP_TITLEID is the titleID of the app stored in the .nacp file (Optional) # ICON is the filename of the icon (.jpg), relative to the project folder. # If not set, it attempts to use one of the following (in this order): # - .jpg # - icon.jpg # - /default_icon.jpg # # CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder. # If not set, it attempts to use one of the following (in this order): # - .json # - config.json # If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead # of a homebrew executable (.nro). This is intended to be used for sysmodules. # NACP building is skipped as well. #--------------------------------------------------------------------------------- TARGET := $(notdir $(CURDIR)) BUILD := build SOURCES := cpp DATA := data INCLUDES := cpp ROMFS := romfs APP_TITLE := is::Engine APP_AUTHOR := author APP_VERSION := 1.0 ICON := icon.jpg PC_LIBS := SDL2_image SDL2_mixer SDL2_ttf sdl2 #--------------------------------------------------------------------------------- # options for code generation #--------------------------------------------------------------------------------- ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE CFLAGS := `$(PREFIX)pkg-config --cflags $(PC_LIBS)` \ -g -Wall -O2 -ffunction-sections \ $(ARCH) $(DEFINES) CFLAGS += $(INCLUDE) -D__SWITCH__ -DIS_ENGINE_SWITCH CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 -std=gnu++17 ASFLAGS := -g $(ARCH) LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) LIBS := `$(PREFIX)pkg-config --libs $(PC_LIBS)` \ -lnx -lm -lstdc++ #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing # include and lib #--------------------------------------------------------------------------------- LIBDIRS := $(PORTLIBS) $(LIBNX) #--------------------------------------------------------------------------------- # no real need to edit anything past this point unless you need to add additional # rules for different file extensions #--------------------------------------------------------------------------------- ifneq ($(BUILD),$(notdir $(CURDIR))) #--------------------------------------------------------------------------------- export OUTPUT := $(CURDIR)/$(TARGET) export TOPDIR := $(CURDIR) export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) export DEPSDIR := $(CURDIR)/$(BUILD) CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) #--------------------------------------------------------------------------------- # use CXX for linking C++ projects, CC for standard C #--------------------------------------------------------------------------------- ifeq ($(strip $(CPPFILES)),) #--------------------------------------------------------------------------------- export LD := $(CC) #--------------------------------------------------------------------------------- else #--------------------------------------------------------------------------------- export LD := $(CXX) #--------------------------------------------------------------------------------- endif #--------------------------------------------------------------------------------- export OFILES_BIN := $(addsuffix .o,$(BINFILES)) export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) export OFILES := $(OFILES_BIN) $(OFILES_SRC) export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ -I$(CURDIR)/$(BUILD) export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) ifeq ($(strip $(CONFIG_JSON)),) jsons := $(wildcard *.json) ifneq (,$(findstring $(TARGET).json,$(jsons))) export APP_JSON := $(TOPDIR)/$(TARGET).json else ifneq (,$(findstring config.json,$(jsons))) export APP_JSON := $(TOPDIR)/config.json endif endif else export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) endif ifeq ($(strip $(ICON)),) icons := $(wildcard *.jpg) ifneq (,$(findstring $(TARGET).jpg,$(icons))) export APP_ICON := $(TOPDIR)/$(TARGET).jpg else ifneq (,$(findstring icon.jpg,$(icons))) export APP_ICON := $(TOPDIR)/icon.jpg endif endif else export APP_ICON := $(TOPDIR)/$(ICON) endif ifeq ($(strip $(NO_ICON)),) export NROFLAGS += --icon=$(APP_ICON) endif ifeq ($(strip $(NO_NACP)),) export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp endif ifneq ($(APP_TITLEID),) export NACPFLAGS += --titleid=$(APP_TITLEID) endif ifneq ($(ROMFS),) export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS) endif .PHONY: $(BUILD) clean all #--------------------------------------------------------------------------------- all: $(BUILD) $(BUILD): @[ -d $@ ] || mkdir -p $@ @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile #--------------------------------------------------------------------------------- clean: @echo clean ... ifeq ($(strip $(APP_JSON)),) @rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf else @rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf endif #--------------------------------------------------------------------------------- else .PHONY: all DEPENDS := $(OFILES:.o=.d) #--------------------------------------------------------------------------------- # main targets #--------------------------------------------------------------------------------- ifeq ($(strip $(APP_JSON)),) all : $(OUTPUT).nro ifeq ($(strip $(NO_NACP)),) $(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp else $(OUTPUT).nro : $(OUTPUT).elf endif else all : $(OUTPUT).nsp $(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm $(OUTPUT).nso : $(OUTPUT).elf endif $(OUTPUT).elf : $(OFILES) $(OFILES_SRC) : $(HFILES_BIN) #--------------------------------------------------------------------------------- # you need a rule like this for each extension you use as binary data #--------------------------------------------------------------------------------- %.bin.o %_bin.h : %.bin #--------------------------------------------------------------------------------- @echo $(notdir $<) @$(bin2o) -include $(DEPENDS) #--------------------------------------------------------------------------------------- endif #--------------------------------------------------------------------------------------- ================================================ FILE: app/src/main/Makefile-vscode ================================================ .SUFFIXES: SUFFIXES = .SUFFIXES: .c .cpp .h .hpp .rc .res .inl .o .d .asm #============================================================================== MAKEFLAGS += --no-print-directory #============================================================================== # Build platform PLATFORM?=linux # Build description (Primarily uses Debug/Release) BUILD?=Release _BUILDL := $(shell echo $(BUILD) | tr A-Z a-z) ifeq ($(BUILD),Tests) _BUILDL := release endif # The sub-folder containing the target source files SRC_TARGET?= ifneq ($(SRC_TARGET),) _SRC_TARGET := /$(SRC_TARGET) endif # Maximum parallel jobs during build process MAX_PARALLEL_JOBS?=8 # Dump assembly? DUMP_ASSEMBLY?=false # Clean output? CLEAN_OUTPUT?=true # If dll, build as a static library? BUILD_STATIC?=false # Platform specific environment variables -include env/.all.mk -include env/.$(_BUILDL).mk -include env/$(PLATFORM).all.mk -include env/$(PLATFORM).$(_BUILDL).mk # Target specific variables ifneq ($(SRC_TARGET),) -include env/$(SRC_TARGET)/.all.mk -include env/$(SRC_TARGET)/.$(_BUILDL).mk -include env/$(SRC_TARGET)/$(PLATFORM).all.mk -include env/$(SRC_TARGET)/$(PLATFORM).$(_BUILDL).mk endif #============================================================================== # File/Folder dependencies for the production build recipe (makeproduction) PRODUCTION_DEPENDENCIES?= # Extensions to exclude from production builds PRODUCTION_EXCLUDE?= # Folder location (relative or absolute) to place the production build into PRODUCTION_FOLDER?=build PRODUCTION_FOLDER_RESOURCES := $(PRODUCTION_FOLDER) #============================================================================== # Library directories (separated by spaces) LIB_DIRS?= INCLUDE_DIRS?= # Link libraries (separated by spaces) LINK_LIBRARIES?= # Precompiled header filename (no extension) # This file will be excluded from Rebuild, but if the bin/(build) directory is removed, it will be as well. PRECOMPILED_HEADER?= # Build-specific preprocessor macros BUILD_MACROS?= # Build-specific compiler flags to be appended to the final build step (with prefix) BUILD_FLAGS?= # Build dependencies to copy into the bin/(build) folder - example: openal32.dll BUILD_DEPENDENCIES?= # NAME should always be passed as an argument from tasks.json as the root folder name, but uses a fallback of "game.exe" # This is used for the output filename (game.exe) NAME?=game.exe #============================================================================== # The source file directory SRC_DIR := cpp$(_SRC_TARGET) LIB_DIR := lib # Project .cpp or .rc files (relative to $(SRC_DIR) directory) SOURCE_FILES := $(patsubst $(SRC_DIR)/%,%,$(shell find $(SRC_DIR) -name '*.cpp' -o -name '*.c' -o -name '*.cc' -o -name '*.rc')) # Project subdirectories within $(SRC_DIR)/ that contain source files PROJECT_DIRS := $(patsubst $(SRC_DIR)/%,%,$(shell find $(SRC_DIR) -mindepth 1 -maxdepth 99 -type d)) # Add prefixes to the above variables _INCLUDE_DIRS := $(patsubst %,-I%,$(SRC_DIR)/ $(LIB_DIR)/ $(INCLUDE_DIRS)) _BUILD_MACROS := $(BUILD_MACROS:%=-D%) _LINK_LIBRARIES := $(LINK_LIBRARIES:%=-l%) #============================================================================== # Unit Testing TEST_DIR := ifeq ($(BUILD),Tests) TEST_DIR := test SOURCE_FILES := $(SOURCE_FILES:Main.cpp=) SOURCE_FILES := $(patsubst $(TEST_DIR)/%,.$(TEST_DIR)/%,$(shell find $(TEST_DIR) -name '*.cpp' -o -name '*.c' -o -name '*.cc' -o -name '*.rc')) $(SOURCE_FILES) _INCLUDE_DIRS := $(patsubst %,-I%,$(TEST_DIR)/) $(_INCLUDE_DIRS) PROJECT_DIRS := .$(TEST_DIR) $(PROJECT_DIRS) BUILD_FLAGS := $(BUILD_FLAGS:-mwindows=) endif #============================================================================== # Linux Specific PRODUCTION_LINUX_ICON?=icon # The full working directory ifeq ($(PLATFORM),linux) _LINUX_GREP_CWD := $(shell echo $(CURDIR) | sed 's/\//\\\//g') endif #============================================================================== # MacOS Specific PRODUCTION_MACOS_ICON?=icon PRODUCTION_MACOS_BUNDLE_COMPANY?=developer PRODUCTION_MACOS_BUNDLE_DISPLAY_NAME?=App PRODUCTION_MACOS_BUNDLE_NAME?=App PRODUCTION_MACOS_MAKE_DMG?=true PRODUCTION_MACOS_BACKGROUND?=dmg-background ifeq ($(PLATFORM),osx) PRODUCTION_MACOS_BUNDLE_COMPANY := '$(PRODUCTION_MACOS_BUNDLE_COMPANY)' PRODUCTION_MACOS_BUNDLE_DISPLAY_NAME := '$(PRODUCTION_MACOS_BUNDLE_DISPLAY_NAME)' PRODUCTION_MACOS_BUNDLE_NAME := '$(PRODUCTION_MACOS_BUNDLE_NAME)' PRODUCTION_FOLDER_MACOS := $(PRODUCTION_FOLDER) PRODUCTION_FOLDER := $(PRODUCTION_FOLDER)/$(PRODUCTION_MACOS_BUNDLE_NAME).app/Contents PRODUCTION_FOLDER_RESOURCES := $(PRODUCTION_FOLDER)/Resources PRODUCTION_DEPENDENCIES := $(PRODUCTION_DEPENDENCIES) PRODUCTION_MACOS_DYLIBS := $(PRODUCTION_MACOS_DYLIBS:%=%.dylib) MACOS_FRAMEWORKS?=CoreFoundation PRODUCTION_MACOS_FRAMEWORKS := $(PRODUCTION_MACOS_FRAMEWORKS:%=%.framework) PRODUCTION_MACOS_BACKGROUND := env/osx/$(PRODUCTION_MACOS_BACKGROUND) MACOS_FRAMEWORK_PATHS := $(MACOS_FRAMEWORK_PATHS:%=-F%) BUILD_FLAGS := $(BUILD_FLAGS) $(MACOS_FRAMEWORK_PATHS) $(MACOS_FRAMEWORKS:%=-framework %) endif #============================================================================== # Directories & Dependencies BLD_DIR := bin-vscode/$(BUILD) ifeq ($(BUILD),Tests) BLD_DIR := bin-vscode/Release endif BLD_DIR := $(BLD_DIR:%/=%) TARGET := $(BLD_DIR)/$(NAME) _NAMENOEXT := $(NAME:.exe=) _NAMENOEXT := $(_NAMENOEXT:.dll=) ifneq ($(SRC_TARGET),) LIB_DIRS := $(LIB_DIRS) $(BLD_DIR) endif _LIB_DIRS := $(LIB_DIR:%=-L%/) $(LIB_DIRS:%=-L%) _SOURCES_IF_RC := $(if $(filter windows,$(PLATFORM)),$(SOURCE_FILES:.rc=.res),$(SOURCE_FILES:%.rc=)) OBJ_DIR := $(BLD_DIR)/obj$(_SRC_TARGET) _OBJS := $(_SOURCES_IF_RC:.c=.c.o) _OBJS := $(_OBJS:.cpp=.cpp.o) _OBJS := $(_OBJS:.cc=.cc.o) OBJS := $(_OBJS:%=$(OBJ_DIR)/%) OBJ_SUBDIRS := $(PROJECT_DIRS:%=$(OBJ_DIR)/%) DEP_DIR := $(BLD_DIR)/dep$(_SRC_TARGET) _DEPS := $(_SOURCES_IF_RC) _DEPS := $(_DEPS:%=%.d) DEPS := $(_DEPS:%=$(DEP_DIR)/%) $(DEP_DIR)/$(PRECOMPILED_HEADER).d DEP_SUBDIRS := $(PROJECT_DIRS:%=$(DEP_DIR)/%) _PCH_HFILE := $(shell find $(SRC_DIR) -name '$(PRECOMPILED_HEADER).hpp' -o -name '$(PRECOMPILED_HEADER).h' -o -name '$(PRECOMPILED_HEADER).hh') _PCH_HFILE := $(_PCH_HFILE:$(SRC_DIR)/%=%) _PCH_EXT := $(_PCH_HFILE:$(PRECOMPILED_HEADER).%=%) _PCH_COMPILER_EXT := $(if $(filter osx,$(PLATFORM)),p,g)ch _SYMBOLS := $(if $(filter osx,$(PLATFORM)),,$(if $(filter Release,$(BUILD)),-s,)) _PCH := $(_PCH_HFILE:%=$(OBJ_DIR)/%) ifneq ($(_PCH),) _PCH_GCH := $(_PCH).$(_PCH_COMPILER_EXT) endif ifeq ($(DUMP_ASSEMBLY),true) ASM_DIR := $(BLD_DIR)/asm$(_SRC_TARGET) _ASMS := $(_OBJS:%.res=) _ASMS := $(_ASMS:.o=.o.asm) ASMS := $(_ASMS:%=$(ASM_DIR)/%) ASM_SUBDIRS := $(PROJECT_DIRS:%=$(ASM_DIR)/%) endif _DIRECTORIES := $(sort bin-vscode $(BLD_DIR) $(OBJ_DIR) $(OBJ_SUBDIRS) $(DEP_DIR) $(DEP_SUBDIRS) $(ASM_DIR) $(ASM_SUBDIRS)) _CLEAN := $(filter true,$(CLEAN_OUTPUT)) # Quiet flag _Q := $(if $(_CLEAN),@) #============================================================================== # Compiler & flags CC?=g++ RC?=windres.exe CFLAGS?=-O2 -Wall -fdiagnostics-color=always CFLAGS_DEPS = -MT $@ -MMD -MP -MF $(DEP_DIR)/$*.Td CFLAGS_DEPS_T = -MT $@ -MMD -MP -MF $(DEP_DIR)/.$(TEST_DIR)/$*.Td PCH_COMPILE = $(CC) $(CFLAGS_DEPS) $(_BUILD_MACROS) $(CFLAGS) $(_INCLUDE_DIRS) -o $@ -c $< ifneq ($(_PCH),) _INCLUDE_PCH := -include $(_PCH) endif OBJ_COMPILE = $(CC) $(CFLAGS_DEPS) $(_BUILD_MACROS) $(_INCLUDE_DIRS) $(_INCLUDE_PCH) $(CFLAGS) -o $@ -c $< OBJ_COMPILE_T = $(CC) $(CFLAGS_DEPS_T) $(_BUILD_MACROS) $(_INCLUDE_DIRS) $(_INCLUDE_PCH) $(CFLAGS) -o $@ -c $< RC_COMPILE = -$(RC) -J rc -O coff -i $< -o $@ ifeq ($(PLATFORM),osx) ASM_COMPILE = otool -tvV $< | c++filt > $@ else ASM_COMPILE = objdump -d -C -Mintel $< > $@ endif POST_COMPILE = mv -f $(DEP_DIR)/$*.Td $(DEP_DIR)/$*.d && touch $@ POST_COMPILE_T = mv -f $(DEP_DIR)/.$(TEST_DIR)/$*.Td $(DEP_DIR)/.$(TEST_DIR)/$*.d && touch $@ #============================================================================== # Build Scripts all: @$(MAKE) makepch @$(MAKE) -j$(MAX_PARALLEL_JOBS) makebuild .DELETE_ON_ERROR: all rebuild: clean all .PHONY: rebuild buildprod: all makeproduction .PHONY: buildprod #============================================================================== # Functions color_reset := @tput setaf 4 define compile_with $(color_reset) $(if $(_CLEAN),@echo ' $($(2):$(OBJ_DIR)/%=%)') $(_Q)$(3) && $(4) endef MKDIR := $(_Q)mkdir -p makepch: $(_PCH_GCH) @echo > /dev/null .PHONY: makepch makebuild: $(TARGET) $(color_reset) ifeq ($(SRC_TARGET),) @echo ' Target is up to date.' else @echo ' $(NAME): Target is up to date.' endif .PHONY: makebuild #============================================================================== # Build Recipes $(OBJ_DIR)/%.o: $(SRC_DIR)/% $(OBJ_DIR)/%.o: $(SRC_DIR)/% $(_PCH_GCH) $(DEP_DIR)/%.d | $(_DIRECTORIES) $(call compile_with,@,<,$(OBJ_COMPILE),$(POST_COMPILE)) $(OBJ_DIR)/.$(TEST_DIR)/%.o: $(TEST_DIR)/% $(OBJ_DIR)/.$(TEST_DIR)/%.o: $(TEST_DIR)/% $(_PCH_GCH) $(DEP_DIR)/.$(TEST_DIR)/%.d | $(_DIRECTORIES) $(call compile_with,@,<,$(OBJ_COMPILE_T),$(POST_COMPILE_T)) $(OBJ_DIR)/%.$(_PCH_EXT).$(_PCH_COMPILER_EXT) : $(SRC_DIR)/%.$(_PCH_EXT) $(OBJ_DIR)/%.$(_PCH_EXT).$(_PCH_COMPILER_EXT) : $(SRC_DIR)/%.$(_PCH_EXT) $(DEP_DIR)/%.d | $(_DIRECTORIES) $(call compile_with,@,<,$(PCH_COMPILE),$(POST_COMPILE)) $(OBJ_DIR)/%.res: $(SRC_DIR)/%.rc $(OBJ_DIR)/%.res: $(SRC_DIR)/%.rc $(DEP_DIR)/%.d | $(_DIRECTORIES) $(color_reset) $(if $(_CLEAN),@echo " $(<:$(OBJ_DIR)/%=%)") $(_Q)$(RC_COMPILE) $(ASM_DIR)/%.o.asm: $(OBJ_DIR)/%.o @tput setaf 6 $(if $(_CLEAN),@echo " $@") $(_Q)$(ASM_COMPILE) $(TARGET): $(_PCH_GCH) $(OBJS) $(ASMS) $(TEST_DIR) $(color_reset) $(if $(_CLEAN),@echo; printf '\xE2\x87\x9B'; echo ' Linking: $(TARGET)') ifeq ($(suffix $(TARGET)),.dll) ifeq ($(BUILD_STATIC),true) -$(_Q)rm -rf $(BLD_DIR)/lib$(_NAMENOEXT).a $(_Q)ar.exe -r -s $(BLD_DIR)/lib$(_NAMENOEXT).a $(OBJS) else -$(_Q)rm -rf $(BLD_DIR)/lib$(_NAMENOEXT).def $(BLD_DIR)/lib$(_NAMENOEXT).a $(_Q)$(CC) -shared -Wl,--output-def="$(BLD_DIR)/lib$(_NAMENOEXT).def" -Wl,--out-implib="$(BLD_DIR)/lib$(_NAMENOEXT).a" -Wl,--dll $(_LIB_DIRS) $(OBJS) -o $@ $(_SYMBOLS) $(_LINK_LIBRARIES) $(BUILD_FLAGS) endif else $(_Q)$(CC) $(_LIB_DIRS) $(_SYMBOLS) -o $@ $(OBJS) $(_LINK_LIBRARIES) $(BUILD_FLAGS) endif @echo ifneq ($(BUILD_DEPENDENCIES),) $(foreach dep,$(BUILD_DEPENDENCIES),$(call copy_to,$(dep),$(BLD_DIR))) endif $(_DIRECTORIES): $(if $(_CLEAN),,$(color_reset)) $(MKDIR) $@ $(if $(_CLEAN),,@echo) clean: $(color_reset) $(if $(_CLEAN),@echo ' Cleaning old build files & folders...'; echo) $(_Q)$(RM) $(TARGET) $(DEPS) $(OBJS) .PHONY: clean #============================================================================== # Production recipes rmprod: $(color_reset) @echo -$(_Q)rm -rf $(if $(filter osx,$(PLATFORM)),$(PRODUCTION_FOLDER_MACOS),$(PRODUCTION_FOLDER)) ifeq ($(PLATFORM),linux) -$(_Q)rm -rf ~/.local/share/applications/$(NAME).desktop endif .PHONY: rmprod mkdirprod: $(color_reset) $(MKDIR) $(PRODUCTION_FOLDER) .PHONY: mkdirprod define do_copy_to_clean @printf "\xE2\x9E\xA6" @echo " Copying \"$(1)\" to \"$(CURDIR)/$(2)\"" $(shell cp -r $(1) $(2)) endef define do_copy_to @echo "cp -r $(1) $(2)" $(shell cp -r $(1) $(2)) endef define copy_to $(if $(wildcard $(2)/$(notdir $(1))),,$(if $(_CLEAN),$(call do_copy_to_clean,$(1),$(2)),$(call do_copy_to,$(1),$(2)))) endef releasetoprod: $(TARGET) $(color_reset) ifeq ($(PLATFORM),osx) @echo ' Creating the MacOS application bundle...' @echo $(MKDIR) $(PRODUCTION_FOLDER)/Resources $(PRODUCTION_FOLDER)/Frameworks $(PRODUCTION_FOLDER)/MacOS ifeq ($(shell brew ls --versions makeicns),) brew install makeicns $(color_reset) endif $(_Q)makeicns -in env/osx/$(PRODUCTION_MACOS_ICON).png -out $(PRODUCTION_FOLDER)/Resources/$(PRODUCTION_MACOS_ICON).icns @echo $(_Q)plutil -convert binary1 env/osx/Info.plist.json -o $(PRODUCTION_FOLDER)/Info.plist $(_Q)plutil -replace CFBundleExecutable -string $(NAME) $(PRODUCTION_FOLDER)/Info.plist $(_Q)plutil -replace CFBundleName -string $(PRODUCTION_MACOS_BUNDLE_NAME) $(PRODUCTION_FOLDER)/Info.plist $(_Q)plutil -replace CFBundleIconFile -string $(PRODUCTION_MACOS_ICON) $(PRODUCTION_FOLDER)/Info.plist $(_Q)plutil -replace CFBundleDisplayName -string "$(PRODUCTION_MACOS_BUNDLE_DISPLAY_NAME)" $(PRODUCTION_FOLDER)/Info.plist $(_Q)plutil -replace CFBundleIdentifier -string com.$(PRODUCTION_MACOS_BUNDLE_DEVELOPER).$(PRODUCTION_MACOS_BUNDLE_NAME) $(PRODUCTION_FOLDER)/Info.plist $(_Q)cp $(TARGET) $(PRODUCTION_FOLDER)/MacOS $(_Q)chmod +x $(PRODUCTION_FOLDER)/MacOS/$(NAME) else ifeq ($(PLATFORM),linux) $(_Q)cp $(TARGET) $(PRODUCTION_FOLDER) $(_Q)cp env/linux/$(PRODUCTION_LINUX_ICON).png $(PRODUCTION_FOLDER)/$(PRODUCTION_LINUX_ICON).png $(_Q)cp env/linux/exec.desktop $(PRODUCTION_FOLDER)/$(NAME).desktop $(_Q)sed -i 's/^Exec=.*/Exec=$(_LINUX_GREP_CWD)\/$(PRODUCTION_FOLDER)\/$(NAME)/' $(PRODUCTION_FOLDER)/$(NAME).desktop $(_Q)sed -i 's/^Path=.*/Path=$(_LINUX_GREP_CWD)\/$(PRODUCTION_FOLDER)/' $(PRODUCTION_FOLDER)/$(NAME).desktop $(_Q)sed -i 's/^Name=.*/Name=$(PRODUCTION_LINUX_APP_NAME)/' $(PRODUCTION_FOLDER)/$(NAME).desktop $(_Q)sed -i 's/^Comment=.*/Comment=$(PRODUCTION_LINUX_APP_COMMENT)/' $(PRODUCTION_FOLDER)/$(NAME).desktop $(_Q)sed -i 's/^Icon=.*/Icon=$(_LINUX_GREP_CWD)\/$(PRODUCTION_FOLDER)\/$(PRODUCTION_LINUX_ICON).png/' $(PRODUCTION_FOLDER)/$(NAME).desktop $(_Q)chmod +x $(PRODUCTION_FOLDER)/$(NAME) $(_Q)chmod +x $(PRODUCTION_FOLDER)/$(NAME).desktop $(_Q)cp $(PRODUCTION_FOLDER)/$(NAME).desktop ~/.local/share/applications else $(_Q)cp $(TARGET) $(PRODUCTION_FOLDER) $(if $(_CLEAN),,@echo) endif .PHONY: releasetoprod makeproduction: rmprod mkdirprod releasetoprod $(color_reset) ifneq ($(PRODUCTION_DEPENDENCIES),) @echo ' Adding dynamic libraries & project dependencies...' @echo $(foreach dep,$(PRODUCTION_DEPENDENCIES),$(call copy_to,$(dep),$(PRODUCTION_FOLDER_RESOURCES))) $(foreach excl,$(PRODUCTION_EXCLUDE),$(shell find $(PRODUCTION_FOLDER_RESOURCES) -name '$(excl)' -delete)) endif ifeq ($(PLATFORM),osx) $(foreach dylib,$(PRODUCTION_MACOS_DYLIBS),$(call copy_to,$(dylib),$(PRODUCTION_FOLDER)/MacOS)) $(_Q)install_name_tool -add_rpath @executable_path/../Frameworks $(PRODUCTION_FOLDER)/MacOS/$(NAME) $(_Q)install_name_tool -add_rpath @loader_path/.. $(PRODUCTION_FOLDER)/MacOS/$(NAME) $(foreach dylib,$(PRODUCTION_MACOS_DYLIBS),$(shell install_name_tool -change $(notdir $(dylib)) @rpath/MacOS/$(notdir $(dylib)) $(PRODUCTION_FOLDER)/MacOS/$(NAME))) $(foreach framework,$(PRODUCTION_MACOS_FRAMEWORKS),$(call copy_to,$(framework),$(PRODUCTION_FOLDER)/Frameworks)) ifeq ($(PRODUCTION_MACOS_MAKE_DMG),true) $(shell hdiutil detach /Volumes/$(PRODUCTION_MACOS_BUNDLE_NAME)/ &> /dev/null) @echo @echo ' Creating the dmg image for the application...' @echo $(_Q)hdiutil create -megabytes 54 -fs HFS+ -volname $(PRODUCTION_MACOS_BUNDLE_NAME) $(PRODUCTION_FOLDER_MACOS)/.tmp.dmg > /dev/null $(_Q)hdiutil attach $(PRODUCTION_FOLDER_MACOS)/.tmp.dmg > /dev/null $(_Q)cp -r $(PRODUCTION_FOLDER_MACOS)/$(PRODUCTION_MACOS_BUNDLE_NAME).app /Volumes/$(PRODUCTION_MACOS_BUNDLE_NAME)/ -$(_Q)rm -rf /Volumes/$(PRODUCTION_MACOS_BUNDLE_NAME)/.fseventsd $(MKDIR) /Volumes/$(PRODUCTION_MACOS_BUNDLE_NAME)/.background $(_Q)tiffutil -cathidpicheck $(PRODUCTION_MACOS_BACKGROUND).png $(PRODUCTION_MACOS_BACKGROUND)@2x.png -out /Volumes/$(PRODUCTION_MACOS_BUNDLE_NAME)/.background/background.tiff $(_Q)ln -s /Applications /Volumes/$(PRODUCTION_MACOS_BUNDLE_NAME)/Applications $(_Q)appName=$(PRODUCTION_MACOS_BUNDLE_NAME) osascript env/osx/dmg.applescript $(_Q)hdiutil detach /Volumes/$(PRODUCTION_MACOS_BUNDLE_NAME)/ > /dev/null $(_Q)hdiutil convert $(PRODUCTION_FOLDER_MACOS)/.tmp.dmg -format UDZO -o $(PRODUCTION_FOLDER_MACOS)/$(PRODUCTION_MACOS_BUNDLE_NAME).dmg > /dev/null $(_Q)rm -f $(PRODUCTION_FOLDER_MACOS)/.tmp.dmg @echo @echo ' Created $(PRODUCTION_FOLDER_MACOS)/$(PRODUCTION_MACOS_BUNDLE_NAME).dmg' endif endif .PHONY: makeproduction #============================================================================== # Dependency recipes $(DEP_DIR)/%.d: ; .PRECIOUS: $(DEP_DIR)/%.d include $(wildcard $(DEPS)) ================================================ FILE: app/src/main/SDL2_SFML.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.14.36408.4 d17.14 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SDL2_SFML", "SDL2_SFML.vcxproj", "{5F4AA128-1C3B-458F-9B6D-7E140CBE20A5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {5F4AA128-1C3B-458F-9B6D-7E140CBE20A5}.Debug|x64.ActiveCfg = Debug|x64 {5F4AA128-1C3B-458F-9B6D-7E140CBE20A5}.Debug|x64.Build.0 = Debug|x64 {5F4AA128-1C3B-458F-9B6D-7E140CBE20A5}.Debug|x86.ActiveCfg = Debug|Win32 {5F4AA128-1C3B-458F-9B6D-7E140CBE20A5}.Debug|x86.Build.0 = Debug|Win32 {5F4AA128-1C3B-458F-9B6D-7E140CBE20A5}.Release|x64.ActiveCfg = Release|x64 {5F4AA128-1C3B-458F-9B6D-7E140CBE20A5}.Release|x64.Build.0 = Release|x64 {5F4AA128-1C3B-458F-9B6D-7E140CBE20A5}.Release|x86.ActiveCfg = Release|Win32 {5F4AA128-1C3B-458F-9B6D-7E140CBE20A5}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {4BC0C116-C5DF-4722-B40C-C405E183D77F} EndGlobalSection EndGlobal ================================================ FILE: app/src/main/SDL2_SFML.vcxproj ================================================ Debug Win32 Release Win32 Debug x64 Release x64 16.0 Win32Proj {5f4aa128-1c3b-458f-9b6d-7e140cbe20a5} SDL2_SFML 10.0 Application true v143 Unicode Application false v143 true Unicode Application true v143 Unicode Application false v143 true Unicode Level3 true IS_ENGINE_SDL_2;IS_ENGINE_VS;_CRT_SECURE_NO_WARNINGS true stdcpp20 Console true Level3 true true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true Console true true true Level3 true IS_ENGINE_SDL_2;IS_ENGINE_VS;_CRT_SECURE_NO_WARNINGS true C:\SDL2-2.26.3\include;%(AdditionalIncludeDirectories) stdcpp20 Console true C:\SDL2-2.26.3\lib\x64;%(AdditionalLibraryDirectories) SDL2.lib;SDL2main.lib;SDL2_image.lib;SDL2_mixer.lib;SDL2_ttf.lib;%(AdditionalDependencies) Level3 true true true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true Console true true true ================================================ FILE: app/src/main/SDL2_SFML.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms {685a88e6-fbc8-479e-8305-bfd706b9dc45} Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Resource Files ================================================ FILE: app/src/main/SDL2_SFML.vcxproj.user ================================================  ================================================ FILE: app/src/main/build.sh ================================================ #!/bin/bash CMD=$1 BUILD=$2 VSCODE=$3 OPTIONS=$4 cwd=${PWD##*/} export GCC_COLORS="error=01;31:warning=01;33:note=01;36:locus=00;34" #============================================================================== # Function declarations display_styled_symbol() { tput setaf $1 tput bold echo "$2 $3" tput sgr0 } build_success() { echo display_styled_symbol 2 "✔" "Succeeded!" echo } launch() { display_styled_symbol 2 " " "Launching bin-vscode/$BUILD/$NAME" echo } build_success_launch() { echo display_styled_symbol 2 "✔" "Succeeded!" launch } build_fail() { echo display_styled_symbol 1 "✘" "Failed!" display_styled_symbol 1 " " "Review the compile errors above." echo tput sgr0 exit 1 } build_prod_error() { echo display_styled_symbol 1 "⭙" "Error: buildprod must be run on Release build." tput sgr0 exit 1 } profiler_done() { echo display_styled_symbol 2 "⯌" "Profiler Completed: View $PROF_ANALYSIS_FILE for details" echo } profiler_error() { echo display_styled_symbol 1 "⭙" "Error: Profiler must be run on Debug build." tput sgr0 exit 1 } profiler_osx() { display_styled_symbol 1 "⭙" "Error: Profiling (with gprof) is not supported on Mac OSX." tput sgr0 exit 1 } buildrun() { display_styled_symbol 3 "⬤" "Build & Run: $BUILD (target: $NAME)" echo BLD=$BUILD if [[ $BUILD == 'Tests' && $1 != 'main' ]]; then BLD=Release fi if $MAKE_EXEC BUILD=$BLD; then build_success_launch if [[ $BUILD == 'Tests' ]]; then bin-vscode/Release/$NAME $OPTIONS else bin-vscode/$BUILD/$NAME $OPTIONS fi else build_fail fi } build() { display_styled_symbol 3 "⬤" "Build: $BUILD (target: $NAME)" echo BLD=$BUILD if [[ $BUILD == 'Tests' && $1 != 'main' ]]; then BLD=Release fi if $MAKE_EXEC BUILD=$BLD; then build_success else build_fail fi } rebuild() { display_styled_symbol 3 "⬤" "Rebuild: $BUILD (target: $NAME)" echo BLD=$BUILD if [[ $BUILD == 'Tests' && $1 != 'main' ]]; then BLD=Release fi if $MAKE_EXEC BUILD=$BLD rebuild; then build_success else build_fail fi } run() { display_styled_symbol 3 "⬤" "Run: $BUILD (target: $NAME)" echo launch if [[ $BUILD == 'Tests' ]]; then bin-vscode/Release/$NAME $OPTIONS else bin-vscode/$BUILD/$NAME $OPTIONS fi } buildprod() { display_styled_symbol 3 "⬤" "Production Build: $BUILD (target: $NAME)" echo if [[ $BUILD == 'Release' ]]; then RECIPE=buildprod if [[ $1 != 'main' ]]; then RECIPE= fi if $MAKE_EXEC BUILD=$BUILD $RECIPE; then build_success else build_fail fi else build_prod_error fi } profile() { display_styled_symbol 3 "⬤" "Profile: $BUILD (target: $NAME)" echo if [[ $PLATFORM == 'osx' ]]; then profiler_osx elif [[ $BUILD == 'Debug' ]]; then if $MAKE_EXEC BUILD=$BUILD; then build_success_launch tput sgr0 bin-vscode/$BUILD/$NAME tput setaf 4 gprof bin-vscode/Debug/$NAME gmon.out > $PROF_ANALYSIS_FILE 2> /dev/null profiler_done else build_fail fi else profiler_error fi } #============================================================================== # Environment if [[ $CMD == '' ]]; then CMD=buildprod fi if [[ $BUILD == '' ]]; then BUILD=Release fi if [[ $OSTYPE == 'linux-gnu'* || $OSTYPE == 'cygwin'* ]]; then if [[ $OSTYPE == 'linux-gnueabihf' ]]; then export PLATFORM=rpi else export PLATFORM=linux fi elif [[ $OSTYPE == 'darwin'* ]]; then export PLATFORM=osx elif [[ $OSTYPE == 'msys' || $OSTYPE == 'win32' ]]; then export PLATFORM=windows fi if [[ $VSCODE != 'vscode' ]]; then export PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin" if [[ $PLATFORM == 'windows' ]]; then export PATH="/c/SFML/bin:/c/mingw32/bin:$PATH" else if [[ $PLATFORM == 'rpi' ]]; then export PATH="/usr/local/gcc-8.1.0/bin:$PATH" fi fi echo echo build.sh PATH=$PATH echo fi export MAKE_EXEC=make if [[ $PLATFORM == 'windows' ]]; then if [ $(type -P "mingw32-make.exe") ]; then export MAKE_EXEC=mingw32-make.exe elif [ $(type -P "make.exe") ]; then export MAKE_EXEC=make.exe fi fi if [[ $BUILD != "Release" && $BUILD != 'Debug' && $BUILD != 'Tests' ]]; then BUILD=Release fi PROF_EXEC=gprof PROF_ANALYSIS_FILE=profiler_analysis.stats #============================================================================== # Main script if [[ $BUILD_TARGETS == '' ]]; then BUILD_TARGETS=main NO_SRC_TARGET=1 fi for target in $BUILD_TARGETS; do if [[ $PLATFORM == 'windows' ]]; then if [[ $target == 'main' ]]; then export NAME=$cwd.exe if [[ $BUILD == 'Tests' ]]; then NAME=tests_$NAME fi else if [[ $BUILD == 'Debug' ]]; then export NAME=$target-d.dll else export NAME=$target.dll fi fi else if [[ $target == 'main' ]]; then export NAME=$cwd if [[ $BUILD == 'Tests' ]]; then NAME=tests_$NAME fi else if [[ $BUILD == 'Debug' ]]; then export NAME=$target-d.so else export NAME=$target.so fi fi fi if [[ $NO_SRC_TARGET != 1 ]]; then export SRC_TARGET=$target fi CHILD_CMD="$CMD $target" if [[ $CMD == 'buildrun' && $target != 'main' ]]; then CHILD_CMD=build fi tput setaf 4 if $CHILD_CMD ; then tput sgr0 else tput setaf 1 tput bold echo $dec Error: Command \"$CHILD_CMD\" not recognized. $dec tput sgr0 exit 1 fi RESULT=$? if [[ $RESULT != 0 ]]; then break fi done exit 0 ================================================ FILE: app/src/main/cmake/app_src.cmake ================================================ # game source file set( app_src # game system extended ${ISENGINE_SRC_DIR}/GameSystemExtended.cpp # game scene # ${ISENGINE_SRC_DIR}/app_src/scenes/... # game level objects # ${ISENGINE_SRC_DIR}/app_src/objects/... # widgets ${ISENGINE_SRC_DIR}/GameDialog.cpp ) ================================================ FILE: app/src/main/cmake/isengine.cmake ================================================ # box 2d source file set( box2d_sources ${ISENGINE_SRC_DIR}/b2BroadPhase.cpp ${ISENGINE_SRC_DIR}/b2CollideCircle.cpp ${ISENGINE_SRC_DIR}/b2CollideEdge.cpp ${ISENGINE_SRC_DIR}/b2CollidePolygon.cpp ${ISENGINE_SRC_DIR}/b2Collision.cpp ${ISENGINE_SRC_DIR}/b2Distance.cpp ${ISENGINE_SRC_DIR}/b2DynamicTree.cpp ${ISENGINE_SRC_DIR}/b2TimeOfImpact.cpp ${ISENGINE_SRC_DIR}/b2CircleShape.cpp ${ISENGINE_SRC_DIR}/b2EdgeShape.cpp ${ISENGINE_SRC_DIR}/b2ChainShape.cpp ${ISENGINE_SRC_DIR}/b2PolygonShape.cpp ${ISENGINE_SRC_DIR}/b2BlockAllocator.cpp ${ISENGINE_SRC_DIR}/b2Draw.cpp ${ISENGINE_SRC_DIR}/b2Math.cpp ${ISENGINE_SRC_DIR}/b2Settings.cpp ${ISENGINE_SRC_DIR}/b2StackAllocator.cpp ${ISENGINE_SRC_DIR}/b2Timer.cpp ${ISENGINE_SRC_DIR}/b2Body.cpp ${ISENGINE_SRC_DIR}/b2ContactManager.cpp ${ISENGINE_SRC_DIR}/b2Fixture.cpp ${ISENGINE_SRC_DIR}/b2Island.cpp ${ISENGINE_SRC_DIR}/b2World.cpp ${ISENGINE_SRC_DIR}/b2WorldCallbacks.cpp ${ISENGINE_SRC_DIR}/b2CircleContact.cpp ${ISENGINE_SRC_DIR}/b2Contact.cpp ${ISENGINE_SRC_DIR}/b2ContactSolver.cpp ${ISENGINE_SRC_DIR}/b2PolygonAndCircleContact.cpp ${ISENGINE_SRC_DIR}/b2EdgeAndCircleContact.cpp ${ISENGINE_SRC_DIR}/b2EdgeAndPolygonContact.cpp ${ISENGINE_SRC_DIR}/b2ChainAndCircleContact.cpp ${ISENGINE_SRC_DIR}/b2ChainAndPolygonContact.cpp ${ISENGINE_SRC_DIR}/b2PolygonContact.cpp ${ISENGINE_SRC_DIR}/b2DistanceJoint.cpp ${ISENGINE_SRC_DIR}/b2FrictionJoint.cpp ${ISENGINE_SRC_DIR}/b2GearJoint.cpp ${ISENGINE_SRC_DIR}/b2Joint.cpp ${ISENGINE_SRC_DIR}/b2MotorJoint.cpp ${ISENGINE_SRC_DIR}/b2MouseJoint.cpp ${ISENGINE_SRC_DIR}/b2PrismaticJoint.cpp ${ISENGINE_SRC_DIR}/b2PulleyJoint.cpp ${ISENGINE_SRC_DIR}/b2RevoluteJoint.cpp ${ISENGINE_SRC_DIR}/b2RopeJoint.cpp ${ISENGINE_SRC_DIR}/b2WeldJoint.cpp ${ISENGINE_SRC_DIR}/b2WheelJoint.cpp ) # engine source file set( commun_sources ${ISENGINE_SRC_DIR}/main.cpp # Basic SFML rendering loop ${ISENGINE_SRC_DIR}/basicSFMLmain.cpp # core ${ISENGINE_SRC_DIR}/GameEngine.cpp # islibconnect ${ISENGINE_SRC_DIR}/isEngineWrapper.cpp ${ISENGINE_SRC_DIR}/isEngineSDLWrapper.cpp # display ${ISENGINE_SRC_DIR}/GameDisplay.cpp # entity ${ISENGINE_SRC_DIR}/MainObject.cpp # graphic ${ISENGINE_SRC_DIR}/TransitionEffect.cpp # function ${ISENGINE_SRC_DIR}/GameFunction.cpp ${ISENGINE_SRC_DIR}/GameKeyData.cpp ${ISENGINE_SRC_DIR}/GameSlider.cpp ${ISENGINE_SRC_DIR}/GameSystem.cpp ${ISENGINE_SRC_DIR}/GameTime.cpp # box 2d ${box2d_sources} ) # tmx lite set( tmxlite_sources ${ISENGINE_SRC_DIR}/isEngine/ext_lib/TMXLite/FreeFuncs.cpp ${ISENGINE_SRC_DIR}/isEngine/ext_lib/TMXLite/ImageLayer.cpp ${ISENGINE_SRC_DIR}/isEngine/ext_lib/TMXLite/LayerGroup.cpp ${ISENGINE_SRC_DIR}/isEngine/ext_lib/TMXLite/Map.cpp ${ISENGINE_SRC_DIR}/isEngine/ext_lib/TMXLite/Object.cpp ${ISENGINE_SRC_DIR}/isEngine/ext_lib/TMXLite/ObjectGroup.cpp ${ISENGINE_SRC_DIR}/isEngine/ext_lib/TMXLite/Property.cpp ${ISENGINE_SRC_DIR}/isEngine/ext_lib/TMXLite/TileLayer.cpp ${ISENGINE_SRC_DIR}/isEngine/ext_lib/TMXLite/Tileset.cpp ${ISENGINE_SRC_DIR}/isEngine/ext_lib/TMXLite/detail/pugixml.cpp ${ISENGINE_SRC_DIR}/isEngine/ext_lib/TMXLite/miniz.c ) # is::Engine Windows & Linux version if (DEFINED ISENGINE_PC) set( isengine ${commun_sources} ${tmxlite_sources} # tiny file dialogs ${ISENGINE_SRC_DIR}/isEngine/ext_lib/TinyFileDialogs/tinyfiledialogs.cpp ) # is::Engine Android version elseif (DEFINED ISENGINE_ANDROID) set( isengine ${commun_sources} ${ISENGINE_SRC_DIR}/SDL_android_main.c #${tmxlite_sources} ) # is::Engine HTML version elseif (DEFINED ISENGINE_HTML_5) set( isengine ${commun_sources} ) endif() ================================================ FILE: app/src/main/cmake/resource.rc ================================================ // application icon MAINICON ICON DISCARDABLE "../env/windows/icon.ico" ================================================ FILE: app/src/main/codeblocks/resource.rc ================================================ // application icon MAINICON ICON "../env/windows/icon.ico" ================================================ FILE: app/src/main/copy_assets.cmd ================================================ @echo off setlocal ENABLEEXTENSIONS REM Define source and destination set "SOURCE=assets" set "DEST=romfs" REM Check if source exists if not exist "%SOURCE%" ( echo [ERROR] The folder '%SOURCE%' does not exist. exit /b 1 ) REM Create destination if it doesn't exist if not exist "%DEST%" ( echo [INFO] The folder '%DEST%' does not exist. Creating it... mkdir "%DEST%" ) REM Copy the 'assets' folder itself (and all contents) into 'data' echo [INFO] Copying the '%SOURCE%' folder into '%DEST%'... xcopy "%SOURCE%" "%DEST%\%SOURCE%\" /E /I /Y /H >nul echo [SUCCESS] The folder '%SOURCE%' has been copied to '%DEST%\%SOURCE%\'. pause ================================================ FILE: app/src/main/cpp/GameDialog.cpp ================================================ #include "app_src/objects/widgets/GameDialog.h" namespace is { GameDialog::GameDialog(sf::Texture &tex, sf::Font &fnt, GameDisplay *m_scene) : MainObject(), m_scene(m_scene), m_showDialog(false), m_mouseInCollison(false), m_dialogEnd(false), m_newLine(true), m_msgIndex(0), m_msgIndexMax(0), m_size(0), m_blindTime(0.f), m_dialogIndex(DIALOG_NONE) { m_strName = "GameDialog"; // object name m_imageScale = 0.f; is::createText(fnt, m_txtDialog, "", m_x, m_y, is::GameConfig::DEFAULT_RPG_DIALOG_TEXT_COLOR, is::GameConfig::DEFAULT_RPG_DIALOG_TEXT_SIZE); is::createText(fnt, m_txtSkip, is::lang::pad_dialog_skip[m_scene->getGameSystem().m_gameLanguage], m_x, m_y, is::GameConfig::DEFAULT_RPG_DIALOG_SELECTED_TEXT_COLOR, true, is::GameConfig::DEFAULT_RPG_DIALOG_BUTTON_TEXT_SIZE); m_strDialog = ""; is::createSprite(tex, m_sprParent, sf::IntRect(0, 0, 480, 96), sf::Vector2f(0.f, 0.f), sf::Vector2f(240.f, 48.f)); is::createSprite(tex, m_sprNext, sf::IntRect(64, 96, 32, 32), sf::Vector2f(0.f, 0.f), sf::Vector2f(16.f, 16.f)); is::createSprite(tex, m_sprSkip, sf::IntRect(0, 96, 64, 24), sf::Vector2f(0.f, 0.f), sf::Vector2f(32.f, 12.f)); is::setSFMLObjScale(m_txtDialog, 0.f); is::centerSFMLObj(m_txtSkip); is::setSFMLObjScale(m_txtSkip, 0.f); is::setSFMLObjScale(m_sprParent, 0.f); is::setSFMLObjScale(m_sprNext, 0.f); } void GameDialog::step(const float &DELTA_TIME) { if (m_showDialog) { if (!m_scene->getGameSystem().keyIsPressed(is::GameConfig::KEY_A) && !m_scene->getGameSystem().isPressed(is::GameSystem::MOUSE)) m_scene->getGameSystem().m_keyIsPressed = false; setPosition(m_scene->getViewX(), m_scene->getViewY() + 32.f); float const _VAL(is::getMSecond(DELTA_TIME)); m_time += (0.8f * is::VALUE_CONVERSION) * DELTA_TIME; m_blindTime += _VAL; if (m_blindTime > 30.f) m_blindTime = 0.f; auto getDialogChar = [this](int index = -1) { int n = m_strDialog.length(); wchar_t* char_array = new wchar_t[n + 1]; for (int i(0); i < n; i++) char_array[i] = m_strDialog[i]; auto my_char = char_array[((index == -1) ? m_size + 1: index)]; delete[] char_array; return my_char; }; linkArrayToEnum(); if (m_size < static_cast(m_strDialog.size()) - 1) { if (m_time > 1.f) { std::wstring tempoStr = m_txtDialog. #if !defined(IS_ENGINE_SFML) getWString(); #else getString(); #endif if (m_newLine) { m_scene->GSMplaySound("change_option"); // We play this sound m_txtDialog.setString(getDialogChar(0)); m_newLine = false; } else { m_txtDialog.setString(tempoStr + getDialogChar()); m_size++; } m_time = 0.f; } } bool mouseInCollisonSkip(false); if (m_scene->mouseCollision(m_sprSkip)) mouseInCollisonSkip = true; if (m_scene->getGameSystem().isPressed(is::GameSystem::ValidationButton::MOUSE) && mouseInCollisonSkip && !m_dialogEnd) { m_scene->GSMplaySound("cancel"); // We play this sound m_scene->getGameSystem().useVibrate(60); m_dialogEnd = true; } m_mouseInCollison = m_scene->mouseCollision(m_sprParent); if (!m_mouseInCollison && m_scene->getGameSystem().isPressed(is::GameSystem::MOUSE)) m_scene->getGameSystem().m_keyIsPressed = true; if ((m_scene->getGameSystem().keyIsPressed(is::GameConfig::KEY_A) || (m_scene->getGameSystem().isPressed(is::GameSystem::ValidationButton::MOUSE) && m_mouseInCollison)) && !m_scene->getGameSystem().m_keyIsPressed && !m_dialogEnd) { m_scene->getGameSystem().m_keyIsPressed = true; m_scene->getGameSystem().useVibrate(60); if (m_size < static_cast(m_strDialog.size()) - 1) { m_txtDialog.setString(m_strDialog); m_size = static_cast(m_strDialog.size()) - 1; } else { m_msgIndex += 2; if (m_msgIndex == m_msgIndexMax) { m_scene->GSMplaySound("cancel"); // We play this sound m_dialogEnd = true; } else { m_newLine = true; m_size = 0; m_txtDialog.setString(""); } } } if (!m_dialogEnd) is::increaseVar(DELTA_TIME, m_imageScale, 0.1f, 1.f, 1.f); else { is::decreaseVar(DELTA_TIME, m_imageScale, 0.1f, 0.f, 0.f); if (m_imageScale < 0.05f) m_showDialog = false; } is::setSFMLObjX_Y(m_sprParent, m_x, m_y - 90.f); is::setSFMLObjX_Y(m_txtDialog, is::getSFMLObjX(m_sprParent) - 225.f, is::getSFMLObjY(m_sprParent) - 38.f); is::setSFMLObjX_Y(m_sprNext, is::getSFMLObjX(m_sprParent) + 220.f, is::getSFMLObjY(m_sprParent) + 29.f); is::setSFMLObjX_Y(m_sprSkip, m_x, m_y + 145.f); is::setSFMLObjX_Y(m_txtSkip, m_x, is::getSFMLObjY(m_sprSkip)); } is::setSFMLObjScale(m_txtDialog, m_imageScale); is::setSFMLObjScale(m_txtSkip, m_imageScale); is::setSFMLObjScale(m_sprSkip, m_imageScale); is::setSFMLObjScale(m_sprParent, m_imageScale); is::setSFMLObjScale(m_sprNext, m_imageScale); } void GameDialog::setDialog(DialogIndex dialogIndex) { m_dialogEnd = false; m_msgIndex = 0; m_size = 0; m_dialogIndex = dialogIndex; m_strDialog = ""; m_txtDialog.setString(""); m_showDialog = true; m_newLine = true; } void GameDialog::setMouseInCollison(bool val) { m_mouseInCollison = val; } void GameDialog::draw(is::Render &surface) { if (m_imageScale > 0.05) { is::draw(surface, m_sprParent); is::draw(surface, m_txtDialog); is::draw(surface, m_txtSkip); is::draw(surface, m_sprSkip); if (m_blindTime < 18.f && m_size == (static_cast(m_strDialog.size()) - 1)) is::draw(surface, m_sprNext); } } } ================================================ FILE: app/src/main/cpp/GameDisplay.cpp ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2024 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/system/display/GameDisplay.h" #include "app_src/language/GameLanguage.h" namespace is { sf::Vector2f getMapPixelToCoords(GameDisplay const *scene, sf::Vector2i pixelPos) { return scene->getRenderWindow().mapPixelToCoords(pixelPos, scene->getView()); } GameDisplay::GameDisplay(GameSystemExtended &gameSysExt, sf::Color bgColor) : m_isClosed(false), m_window(gameSysExt.m_window), m_view(sf::Vector2f(is::GameConfig::VIEW_WIDTH / 2.f, is::GameConfig::VIEW_HEIGHT / 2.f), sf::Vector2f(is::GameConfig::VIEW_WIDTH, is::GameConfig::VIEW_HEIGHT)), m_surface(gameSysExt.m_window), m_gameSysExt(gameSysExt), m_timeVibrateDuration(40), m_optionIndex(0), m_waitTime(0), m_msgWaitTime(0), m_sceneWidth(is::GameConfig::VIEW_WIDTH), m_sceneHeight(is::GameConfig::VIEW_HEIGHT), DELTA_TIME(0.f), m_viewW(is::GameConfig::VIEW_WIDTH), m_viewH(is::GameConfig::VIEW_HEIGHT), m_viewX(m_viewW / 2.f), m_viewY(m_viewH / 2.f), m_sprButtonSelectScale(1.f), m_isRunning(true), m_windowIsActive(true), m_isPlaying(true), m_sceneStart(true), m_sceneEnd(false), m_keyBackPressed(false), m_showMsg(false), m_mbYesNo(false), m_msgBoxMouseInCollision(false), m_mouseInCollision(false) { setViewSize(m_viewW, m_viewH); setView(m_viewX, m_viewY); m_windowBgColor = bgColor; } GameDisplay::~GameDisplay() {} void GameDisplay::setOptionIndex(int optionIndexValue, bool callWhenClick, float buttonScale) { if (m_waitTime == 0) { m_gameSysExt.useVibrate(m_timeVibrateDuration); GSMplaySound("change_option"); m_sprButtonSelectScale = buttonScale; if (!callWhenClick) { m_optionIndex += optionIndexValue; m_gameSysExt.m_keyIsPressed = true; } else m_optionIndex = optionIndexValue; } } void GameDisplay::setOptionIndex(int optionIndexValue) { m_optionIndex = optionIndexValue; } void GameDisplay::setTextAnimation(sf::Text &txt, sf::Sprite &spr, int val) { if (m_optionIndex == val) { is::setSFMLObjX_Y(m_sprButtonSelect, is::getSFMLObjX(spr), is::getSFMLObjY(spr)); is::setSFMLObjFillColor(txt, is::GameConfig::DEFAULT_SFML_SELECTED_TEXT_COLOR); } else is::setSFMLObjFillColor(txt, is::GameConfig::DEFAULT_SFML_TEXT_COLOR); } void GameDisplay::setSprButtonSelectScale(float val) { m_sprButtonSelectScale = val; } void GameDisplay::setView(sf::Vector2f v) { m_view.setCenter(v.x, v.y); m_surface.setView(m_view); } void GameDisplay::setView(float x, float y) { m_view.setCenter(x, y); m_surface.setView(m_view); } void GameDisplay::setView() { m_view.setCenter(m_viewX, m_viewY); m_surface.setView(m_view); } void GameDisplay::setViewVarX(float val) { m_viewX = val; } void GameDisplay::setViewVarY(float val) { m_viewY = val; } void GameDisplay::setViewVarXY(float x, float y) { m_viewX = x; m_viewY = y; } void GameDisplay::setViewSize(sf::Vector2f v) { m_view.setSize(v.x, v.y); } void GameDisplay::setViewSize(float x, float y) { m_view.setSize(x, y); } void GameDisplay::setWindowSize(sf::Vector2u v, bool updateViewSize) { #if defined(__ANDROID__) m_window.setSize(v); if (updateViewSize) { m_viewW = v.x; m_viewH = v.y; m_viewX = m_viewW / 2.f; m_viewY = m_viewH / 2.f; m_view.setSize(m_viewW, m_viewH); m_view.setCenter(m_viewX, m_viewY); m_window.setView(m_view); m_surface.setView(m_view); } #endif } void GameDisplay::setWindowTitle(const std::string &title) { m_window.setTitle(title); } void GameDisplay::setWindowBgColor(sf::Color color) { m_windowBgColor = color; } void GameDisplay::controlEventFocusClosing(sf::Event &event) { // Manage the state of window if (event.type == sf::Event::GainedFocus) m_windowIsActive = true; if (event.type == sf::Event::LostFocus) m_windowIsActive = false; // Closing the application if (event.type == sf::Event::Closed) { m_isRunning = false; // quit the main render loop m_window.close(); } } void GameDisplay::showMessageBox(const std::string &msgBody, bool mbYesNo) { setMessageBoxData(mbYesNo); m_txtMsgBox.setString(msgBody); } void GameDisplay::showMessageBox(std::wstring const &msgBody, bool mbYesNo) { setMessageBoxData(mbYesNo); m_txtMsgBox.setString(msgBody); } void GameDisplay::setWidgetsPosition() { setSFMLObjX_Y(m_recMsgBox, sf::Vector2f(m_view.getCenter().x, m_view.getCenter().y)); setSFMLObjX_Y(m_sprMsgBox, sf::Vector2f(m_view.getCenter().x, m_view.getCenter().y)); const float dim(6.f), boxXOrigin(is::getSFMLObjOriginX(m_sprMsgBox)), boxYOrigin(is::getSFMLObjOriginY(m_sprMsgBox)); setSFMLObjX_Y(m_sprMsgBoxButton1, is::getSFMLObjX(m_sprMsgBox) - boxXOrigin + is::getSFMLObjOriginX(m_sprMsgBoxButton1) + dim, is::getSFMLObjY(m_sprMsgBox) + boxYOrigin - is::getSFMLObjHeight(m_sprMsgBoxButton1) + dim); setSFMLObjX_Y(m_sprMsgBoxButton2, is::getSFMLObjX(m_sprMsgBox) + boxXOrigin - is::getSFMLObjOriginX(m_sprMsgBoxButton2) - dim, is::getSFMLObjY(m_sprMsgBox) + boxYOrigin - is::getSFMLObjHeight(m_sprMsgBoxButton2) + dim); setSFMLObjX_Y(m_sprMsgBoxButton3, is::getSFMLObjX(m_sprMsgBox), is::getSFMLObjY(m_sprMsgBox) + boxYOrigin - is::getSFMLObjHeight(m_sprMsgBoxButton1) + dim); setSFMLObjX_Y(m_txtMsgBox, is::getSFMLObjX(m_sprMsgBox) - boxXOrigin + 16.f, is::getSFMLObjY(m_sprMsgBox) - boxYOrigin + 8.f); // Adjust the text on button setSFMLObjX_Y(m_txtMsgBoxYes, is::getSFMLObjX(m_sprMsgBoxButton1), is::getSFMLObjY(m_sprMsgBoxButton1) #if defined(IS_ENGINE_SFML) - is::getSFMLObjHeight(m_txtMsgBoxYes) / 4.f #endif ); setSFMLObjX_Y(m_txtMsgBoxNo, is::getSFMLObjX(m_sprMsgBoxButton2), is::getSFMLObjY(m_sprMsgBoxButton2) #if defined(IS_ENGINE_SFML) - is::getSFMLObjHeight(m_txtMsgBoxNo) / 4.f #endif ); setSFMLObjX_Y(m_txtMsgBoxOK, is::getSFMLObjX(m_sprMsgBoxButton3), is::getSFMLObjY(m_sprMsgBoxButton3) #if defined(IS_ENGINE_SFML) - is::getSFMLObjHeight(m_txtMsgBoxOK) / 4.f #endif ); } void GameDisplay::setMessageBoxData(bool mbYesNo) { m_showMsg = true; m_mbYesNo = mbYesNo; if (m_mbYesNo) m_msgAnswer = MsgAnswer::NO; m_msgWaitTime = 0; m_msgBoxMouseInCollision = false; m_txtMsgBoxYes.setString(is::lang::pad_answer_yes[m_gameSysExt.m_gameLanguage]); m_txtMsgBoxNo.setString(is::lang::pad_answer_no[m_gameSysExt.m_gameLanguage]); m_txtMsgBoxOK.setString(is::lang::pad_answer_ok[m_gameSysExt.m_gameLanguage]); centerSFMLObj(m_txtMsgBoxYes); centerSFMLObj(m_txtMsgBoxNo); centerSFMLObj(m_txtMsgBoxOK); setView(); setWidgetsPosition(); is::setSFMLObjAlpha(m_sprMsgBoxButton1, m_msgWaitTime); is::setSFMLObjAlpha(m_sprMsgBoxButton2, m_msgWaitTime); is::setSFMLObjAlpha(m_sprMsgBoxButton3, m_msgWaitTime); is::setSFMLObjAlpha(m_sprMsgBox, m_msgWaitTime); is::setSFMLObjAlpha2(m_txtMsgBoxNo, m_msgWaitTime); is::setSFMLObjAlpha2(m_txtMsgBoxYes, m_msgWaitTime); is::setSFMLObjAlpha2(m_txtMsgBoxOK, m_msgWaitTime); is::setSFMLObjAlpha2(m_txtMsgBox, m_msgWaitTime); } void GameDisplay::updateMsgBox(int sliderDirection, bool rightSideValidation, sf::Color textDefaultColor, sf::Color selectedTextColor) { if (m_msgWaitTime < 240) m_msgWaitTime += static_cast((8.f * is::VALUE_CONVERSION) * DELTA_TIME); else m_msgWaitTime = 255; if (!m_gameSysExt.isPressed()) m_gameSysExt.m_keyIsPressed = false; // Check collision with all objects of message box if (mouseCollision(m_sprMsgBoxButton1, m_mousePosCurrent) || mouseCollision(m_sprMsgBoxButton2, m_mousePosCurrent) || mouseCollision(m_sprMsgBoxButton3)) m_msgBoxMouseInCollision = true; else m_msgBoxMouseInCollision = false; /* * sliderDirection is the enum variable found in is::GameSlider. It was not called from the instance * because its use is not mandatory in a Scene. This avoids the error message which implies that the * instance has not been declared because here we have implemented are not used in even if it does not exist * These different values (represents the enum of the class) SLIDE_NONE = 0, SLIDE_UP = 1, SLIDE_DOWN = 2, SLIDE_RIGHT = 3, SLIDE_LEFT = 4 */ // Avoid the long pressing button effect if (!m_msgBoxMouseInCollision && sliderDirection == 0 && m_gameSysExt.isPressed(is::GameSystem::MOUSE)) m_gameSysExt.m_keyIsPressed = true; if (m_msgWaitTime == 255 && m_windowIsActive) { // If it's YES / NO message box if (m_mbYesNo) { if ((m_gameSysExt.keyIsPressed(is::GameConfig::KEY_LEFT) || (sliderDirection == 4) || (mouseCollision(m_sprMsgBoxButton1, m_mousePosCurrent) && m_mousePosPrevious != m_mousePosCurrent)) && m_msgAnswer != MsgAnswer::YES) { if (m_msgBoxMouseInCollision) m_mousePosPrevious = m_mousePosCurrent; m_gameSysExt.useVibrate(m_timeVibrateDuration); GSMplaySound("change_option"); m_msgAnswer = MsgAnswer::YES; // answer = yes } else if ((m_gameSysExt.keyIsPressed(is::GameConfig::KEY_RIGHT) || (sliderDirection == 3) || (mouseCollision(m_sprMsgBoxButton2, m_mousePosCurrent) && m_mousePosPrevious != m_mousePosCurrent)) && m_msgAnswer != MsgAnswer::NO) { if (m_msgBoxMouseInCollision) m_mousePosPrevious = m_mousePosCurrent; m_gameSysExt.useVibrate(m_timeVibrateDuration); GSMplaySound("change_option"); m_msgAnswer = MsgAnswer::NO; // answer = no } else if (m_gameSysExt.isPressed(is::GameSystem::KEYBOARD) || (rightSideValidation) || ((mouseCollision(m_sprMsgBoxButton1, m_mousePosCurrent) || mouseCollision(m_sprMsgBoxButton2, m_mousePosCurrent)) && m_gameSysExt.isPressed(is::GameSystem::MOUSE) && !m_gameSysExt.m_keyIsPressed)) { m_showMsg = false; m_gameSysExt.m_keyIsPressed = true; } else if (m_keyBackPressed) { m_msgAnswer = MsgAnswer::NO; // answer = no (canceled) m_showMsg = false; m_keyBackPressed = false; } // Texts animations if (m_msgAnswer == MsgAnswer::YES) { is::setSFMLObjFillColor(m_txtMsgBoxYes, selectedTextColor); is::setSFMLObjFillColor(m_txtMsgBoxNo, textDefaultColor); } else { is::setSFMLObjFillColor(m_txtMsgBoxNo, selectedTextColor); is::setSFMLObjFillColor(m_txtMsgBoxYes, textDefaultColor); } } else // If it's OK message box { if (mouseCollision(m_sprMsgBoxButton3) && m_msgAnswer == MsgAnswer::NO) { m_gameSysExt.useVibrate(m_timeVibrateDuration); GSMplaySound("change_option"); m_msgAnswer = MsgAnswer::YES; // answer = OK is::setSFMLObjFillColor(m_txtMsgBoxOK, selectedTextColor); } else if (((m_gameSysExt.isPressed(is::GameSystem::KEYBOARD) || m_keyBackPressed) && !mouseCollision(m_sprMsgBoxButton3)) || (rightSideValidation)|| (mouseCollision(m_sprMsgBoxButton3) && m_gameSysExt.isPressed(is::GameSystem::MOUSE) && !m_gameSysExt.m_keyIsPressed)) { m_showMsg = false; m_keyBackPressed = false; m_gameSysExt.m_keyIsPressed = true; } else if (!mouseCollision(m_sprMsgBoxButton3) && m_msgAnswer == MsgAnswer::YES) { m_msgAnswer = MsgAnswer::NO; // answer = NO is::setSFMLObjFillColor(m_txtMsgBoxOK, textDefaultColor); } } } if (m_msgWaitTime != 255) { if (m_mbYesNo) { is::setSFMLObjColor(m_sprMsgBoxButton1, sf::Color(255, 255, 255, m_msgWaitTime)); is::setSFMLObjColor(m_sprMsgBoxButton2, sf::Color(255, 255, 255, m_msgWaitTime)); is::setSFMLObjFillColor(m_txtMsgBoxNo, sf::Color(selectedTextColor.r, selectedTextColor.g, selectedTextColor.b, m_msgWaitTime)); is::setSFMLObjFillColor(m_txtMsgBoxYes, sf::Color(textDefaultColor.r, textDefaultColor.g, textDefaultColor.b, m_msgWaitTime)); } else { is::setSFMLObjColor(m_sprMsgBoxButton3, sf::Color(255, 255, 255, m_msgWaitTime)); is::setSFMLObjFillColor(m_txtMsgBoxOK, sf::Color(textDefaultColor.r, textDefaultColor.g, textDefaultColor.b, m_msgWaitTime)); } } is::setSFMLObjColor(m_sprMsgBox, sf::Color(255, 255, 255, m_msgWaitTime)); is::setSFMLObjFillColor(m_txtMsgBox, sf::Color(textDefaultColor.r, textDefaultColor.g, textDefaultColor.b, m_msgWaitTime)); if (!m_showMsg) { if (m_msgAnswer == MsgAnswer::NO) { // If is OK message box the answer is automatically YES if (!m_mbYesNo) { m_msgAnswer = MsgAnswer::YES; GSMplaySound("select_option"); m_gameSysExt.useVibrate(m_timeVibrateDuration); } else GSMplaySound("cancel"); } else { GSMplaySound("select_option"); m_gameSysExt.useVibrate(m_timeVibrateDuration); } } } void GameDisplay::updateTimeWait() { // Waiting time before validating an option if (m_waitTime > 0) { m_waitTime -= is::getMSecond(DELTA_TIME); } else m_waitTime = 0; } void GameDisplay::drawMsgBox() { if (m_showMsg) { is::draw(m_surface, m_recMsgBox); is::draw(m_surface, m_sprMsgBox); if (m_mbYesNo) { is::draw(m_surface, m_sprMsgBoxButton1); is::draw(m_surface, m_sprMsgBoxButton2); is::draw(m_surface, m_txtMsgBoxYes); is::draw(m_surface, m_txtMsgBoxNo); } else { is::draw(m_surface, m_sprMsgBoxButton3); is::draw(m_surface, m_txtMsgBoxOK); } is::draw(m_surface, m_txtMsgBox); } } void GameDisplay::drawScreen() { is::clear(m_surface, m_windowBgColor); #if defined(__ANDROID__) // On Android when the window is no longer active, nothing is displayed just a black screen. // Its allows to optimize the application if (m_windowIsActive) { #endif draw(); #if defined(__ANDROID__) } #endif is::display(m_window); } void GameDisplay::showTempLoading(float time) { float timeToQuit(0.f); sf::Sprite sprTmploading, sprTmploading2; is::createSprite(GRMgetTexture("temp_loading"), sprTmploading, sf::Vector2f(m_viewX, m_viewY), sf::Vector2f(320.f, 240.f)); is::createSprite(GRMgetTexture("loading_icon"), sprTmploading2, sf::Vector2f(m_viewX, m_viewY), sf::Vector2f(16.f, 16.f)); while (timeToQuit < time) { float dTime = getDeltaTime(); timeToQuit += is::getMSecond(dTime); sprTmploading2.rotate((5.f * is::VALUE_CONVERSION) * dTime); sf::Event ev; while (m_window.pollEvent(ev)) { if (ev.type == sf::Event::Closed) is::closeApplication(); } is::clear(m_window, sf::Color::Black); is::draw(m_surface, sprTmploading); is::draw(m_surface, sprTmploading2); is::display(m_window); } } void GameDisplay::loadParentResources() { if (!m_gameSysExt.m_loadParentResources) { // Load sound m_gameSysExt.GSMaddSound("change_option", is::GameConfig::SFX_DIR + "change_option" + SND_FILE_EXTENSION); m_gameSysExt.GSMaddSound("cancel", is::GameConfig::SFX_DIR + "cancel" + SND_FILE_EXTENSION); m_gameSysExt.GSMaddSound("select_option", is::GameConfig::SFX_DIR + "select_option" + SND_FILE_EXTENSION); // Load message box sprite m_gameSysExt.GRMaddTexture("confirm_box", is::GameConfig::GUI_DIR + "confirm_box.png"); m_gameSysExt.GRMaddTexture("confirm_box_button", is::GameConfig::GUI_DIR + "confirm_box_button.png"); // Temporal loading texture m_gameSysExt.GRMaddTexture("temp_loading", is::GameConfig::GUI_DIR + "temp_loading.png"); m_gameSysExt.GRMaddTexture("loading_icon", is::GameConfig::GUI_DIR + "loading_icon.png"); // Load font m_gameSysExt.GRMaddFont("font_system", GameConfig::FONT_DIR + "font_system.ttf"); m_gameSysExt.GRMaddFont("font_msg", GameConfig::FONT_DIR + "font_msg.ttf"); m_gameSysExt.m_loadParentResources = true; } if (m_gameSysExt.m_loadParentResources) { GRMuseGameSystemFont(); GRMuseGameSystemTexture(); GSMuseGameSystemSound(); } auto &texMsgButton = GRMgetTexture("confirm_box_button"); is::createSprite(GRMgetTexture("confirm_box"), m_sprMsgBox, sf::Vector2f(0.f, 0.f), sf::Vector2f(0.f, 0.f)); is::createSprite(texMsgButton, m_sprMsgBoxButton1, sf::Vector2f(0.f, 0.f), sf::Vector2f(0.f, 0.f)); is::createSprite(texMsgButton, m_sprMsgBoxButton2, sf::Vector2f(0.f, 0.f), sf::Vector2f(0.f, 0.f)); is::createSprite(texMsgButton, m_sprMsgBoxButton3, sf::Vector2f(0.f, 0.f), sf::Vector2f(0.f, 0.f)); is::createRectangle(m_recMsgBox, sf::Vector2f(m_viewW + 40.f, m_viewH + 40.f), sf::Color(0, 0, 0, 200), 0.f, 0.f); is::centerSFMLObj(m_sprMsgBox); is::centerSFMLObj(m_sprMsgBoxButton1); is::centerSFMLObj(m_sprMsgBoxButton2); is::centerSFMLObj(m_sprMsgBoxButton3); // Load font auto &fontSystem = GRMgetFont("font_system"); is::createText(fontSystem, m_txtMsgBox, "", 0.f, 0.f, is::GameConfig::DEFAULT_MSG_BOX_TEXT_SIZE); #if defined(IS_ENGINE_SDL_2) m_txtMsgBox.m_SDLaddTextRecWSize += 32; #endif is::createText(fontSystem, m_txtMsgBoxYes, is::lang::pad_answer_yes[m_gameSysExt.m_gameLanguage], 0.f, 0.f, true, is::GameConfig::DEFAULT_MSG_BOX_BUTTON_TEXT_SIZE); is::createText(fontSystem, m_txtMsgBoxNo, is::lang::pad_answer_no[m_gameSysExt.m_gameLanguage], 0.f, 0.f, true, is::GameConfig::DEFAULT_MSG_BOX_BUTTON_TEXT_SIZE); is::createText(fontSystem, m_txtMsgBoxOK, is::lang::pad_answer_ok[m_gameSysExt.m_gameLanguage], 0.f, 0.f, true, is::GameConfig::DEFAULT_MSG_BOX_BUTTON_TEXT_SIZE); is::createSprite(GRMgetTexture("temp_loading"), m_sprLoading, sf::Vector2f(m_viewX, m_viewY), sf::Vector2f(320.f, 240.f)); } void GameDisplay::setIsRunning(bool val) { m_isRunning = val; } void GameDisplay::setIsPlaying(bool val) { m_isPlaying = val; } void GameDisplay::quitScene(int nextScene) { if (nextScene != -1) { m_gameSysExt.m_launchOption = static_cast(nextScene); m_isRunning = false; } else is::closeApplication(); } void GameDisplay::setWaitTime(int val) { m_waitTime = val; } void GameDisplay::setSceneStart(bool val) { m_sceneStart = val; } void GameDisplay::setSceneEnd(bool val) { m_sceneEnd = val; } void GameDisplay::setKeyBackPressed(bool val) { m_keyBackPressed = val; } void GameDisplay::setMouseInCollision() { if (m_mousePosCurrent != m_mousePosPrevious) { is::setVector2(m_mousePosPrevious, m_mousePosCurrent.x, m_mousePosCurrent.y); m_mouseInCollision = true; } } float GameDisplay::getDeltaTime() { float dt = m_clock.restart().asSeconds(); if (dt > is::MAX_CLOCK_TIME) dt = is::MAX_CLOCK_TIME; return dt; } sf::Vector2f GameDisplay::getCursor(unsigned int finger) const { return is::getCursor(m_window, finger); } bool GameDisplay::getMouseCurrentEqualToPrevious() { return (m_mousePosCurrent == m_mousePosPrevious); } bool GameDisplay::inViewRec(is::MainObject *obj, bool useTexRec) { is::Rectangle testRec; if (useTexRec) { testRec.m_left = obj->getX(); testRec.m_top = obj->getY(); testRec.m_right = obj->getX() + is::getSFMLObjWidth(obj->getSprite()); testRec.m_bottom = obj->getY() + is::getSFMLObjHeight(obj->getSprite()); } else testRec = obj->getMask(); bool isCollision = false; is::Rectangle viewRec; viewRec.m_left = getViewX() - (getViewW() / 2) - 16; viewRec.m_right = getViewX() + (getViewW() / 2) + 16; viewRec.m_top = getViewY() - (getViewH() / 2); viewRec.m_bottom = getViewY() + (getViewH() / 2); if (is::collisionTest(testRec, viewRec)) { isCollision = true; } return isCollision; } bool GameDisplay::inViewRec(is::MainObject &obj, bool useTexRec) { return (inViewRec(&obj, useTexRec)); } bool GameDisplay::getIsRunning() const { return m_isRunning; } #if defined(IS_ENGINE_USE_SDM) void GameDisplay::SDMmanageScene() { DELTA_TIME = getDeltaTime(); updateTimeWait(); // even loop SDMmanageSceneEvents(); // starting mechanism if (m_sceneStart) { // window has focus if (m_windowIsActive) { if (!m_showMsg) { SDMstep(); } ////////////////////////////////////////////////////////////////////////////////////////////////////// // MESSAGE BOX ////////////////////////////////////////////////////////////////////////////////////////////////////// else { updateMsgBox(0, false); // when user closes message box in update function execute this instruction // "m_waitTime" allow to disable clicks on objects during a moment when user closes message box if (!m_showMsg) SDMmanageSceneMsgAnswers(); } } } } void GameDisplay::SDMmanageSceneEvents() { sf::Event event; while (m_window.pollEvent(event)) // even loop { controlEventFocusClosing(event); if (m_gameSysExt.keyIsPressed(is::GameConfig::KEY_CANCEL)) { if (!m_showMsg) showMessageBox(is::lang::msg_quit_game[m_gameSysExt.m_gameLanguage]); else if (m_msgWaitTime == 255) /* Allows to close the message box with the Cancel key when it is visible*/ m_keyBackPressed = true; } SDMcallObjectsEvents(event); } } void GameDisplay::SDMmanageSceneMsgAnswers() { if (m_msgAnswer == MsgAnswer::YES) // if answers is YES close application { m_window.close(); m_isRunning = false; } else // if answers is NO continue execution { m_waitTime = 20; } } void GameDisplay::SDMcallObjectsEvents(sf::Event &event) { if (m_SDMObjectsEvent) { // call objects events for (std::list>::iterator it = m_SDMsceneObjects.begin(); it != m_SDMsceneObjects.end(); ++it) { if (is::instanceExist(*it)) { if ((*it)->m_SDMcallEvent) { (*it)->event(event); } } } } } void GameDisplay::SDMstep() { if (m_SDMObjectsStep) { // update scene objects for (std::list>::iterator it = m_SDMsceneObjects.begin(); it != m_SDMsceneObjects.end(); ++it) { if (is::instanceExist(*it)) { if ((*it)->m_SDMcallStep) { (*it)->step(DELTA_TIME); } if (*it != nullptr) { if ((*it)->isDestroyed()) { it->reset(); } } } } if (m_SDMsortArray) { is::sortObjArrayByDepth(m_SDMsceneObjects); m_SDMsortArray = false; } } } void GameDisplay::SDMdraw() { if (m_SDMObjectsDraw) { #if defined(IS_ENGINE_SDL_2) std::shared_ptr obj = nullptr; #endif // draw scene objects for (std::list>::iterator it = m_SDMsceneObjects.begin(); it != m_SDMsceneObjects.end(); ++it) { if (is::instanceExist(*it)) { if ((*it)->m_SDMcallDraw) { if ((*it)->m_SDMblitSprTextureName != "") { #if defined(IS_ENGINE_SDL_2) if (obj.get() != nullptr) { if (obj->m_strTextureName != (*it)->m_SDMblitSprTextureName) { for (unsigned int i(0); i < m_SDMblitSDLSprite.size(); ++i) { if (m_SDMblitSDLSprite[i]->m_strTextureName == (*it)->m_SDMblitSprTextureName) { obj = m_SDMblitSDLSprite[i]; break; } } } } else { for (unsigned int i(0); i < m_SDMblitSDLSprite.size(); ++i) { if (m_SDMblitSDLSprite[i]->m_strTextureName == (*it)->m_SDMblitSprTextureName) { obj = m_SDMblitSDLSprite[i]; break; } } } obj->m_sprBlit.setTextureRect(sf::IntRect((*it)->getSprite().getTextureRect().left, (*it)->getSprite().getTextureRect().top, (*it)->getSprite().getTextureRect().width, (*it)->getSprite().getTextureRect().height)); obj->m_sprBlit.setPosition(is::getSFMLObjX((*it)->getSprite()), is::getSFMLObjY((*it)->getSprite())); obj->m_sprBlit.setOrigin(is::getSFMLObjOriginX((*it)->getSprite()), is::getSFMLObjOriginY((*it)->getSprite())); obj->m_sprBlit.setScale(is::getSFMLObjXScale((*it)->getSprite()), is::getSFMLObjYScale((*it)->getSprite())); obj->m_sprBlit.setColor((*it)->getSprite().getColor().r, (*it)->getSprite().getColor().g, (*it)->getSprite().getColor().b, (*it)->getSprite().getColor().a); #endif if (inViewRec(it->get(), true)) { if ((*it)->getVisible()) m_window.draw( #if defined(IS_ENGINE_SDL_2) obj->m_sprBlit #else (*it)->getSprite() #endif ); } } else (*it)->draw(m_surface); } } } } drawMsgBox(); } void GameDisplay::createSprite(const std::string &spriteName, is::MainObject &obj, sf::IntRect rec, sf::Vector2f position, sf::Vector2f origin, sf::Vector2f scale, unsigned int alpha) { auto &tex = GRMgetTexture(spriteName); obj.m_SDMblitSprTextureName = #if !defined(IS_ENGINE_SDL_2) spriteName; is::createSprite(tex, obj.getSprite(), rec, sf::Vector2f(position.x, position.y), sf::Vector2f(origin.x, origin.y), false, false); #else GRMgetTexture(spriteName).getFileName(); bool exists = false; for (unsigned int i(0); i < m_SDMblitSDLSprite.size(); ++i) { if (m_SDMblitSDLSprite[i]->m_strTextureName == obj.m_SDMblitSprTextureName) { exists = true; break; } } if (!exists) m_SDMblitSDLSprite.push_back(std::make_shared(obj.m_SDMblitSprTextureName, tex)); obj.getSprite().setTextureRect(sf::IntRect(rec.left, rec.top, rec.width, rec.height)); obj.getSprite().setPosition(position.x, position.y); obj.getSprite().setOrigin(origin.x, origin.y); obj.getSprite().setScale(scale.x, scale.y); obj.getSprite().setColor(255, 255, 255, alpha); #endif } #endif } ================================================ FILE: app/src/main/cpp/GameEngine.cpp ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/core/GameEngine.h" #if defined(IS_ENGINE_HTML_5) std::function mainLoop; void MainLoop() { return mainLoop(); } #endif namespace is { GameEngine::GameEngine(): m_gameSysExt(m_window) {} GameEngine::~GameEngine() { #if defined(IS_ENGINE_SDL_2) is::SDL2freeLib(); #endif }; void GameEngine::initEngine() { m_gameSysExt.initSystemData(); m_window.create(sf::VideoMode(is::GameConfig::WINDOW_WIDTH, is::GameConfig::WINDOW_HEIGHT), is::GameConfig::GAME_NAME, is::getWindowStyle()); #if !defined(__ANDROID__) #if defined(IS_ENGINE_SFML) // load application icon sf::Image iconTex; if (iconTex.loadFromFile(is::GameConfig::GUI_DIR + "icon.png")) m_window.setIcon(iconTex.getSize().x, iconTex.getSize().y, iconTex.getPixelsPtr()); #endif // create saving directory #if (!defined(IS_ENGINE_HTML_5) && !defined(IS_ENGINE_SWITCH)) if (!m_gameSysExt.fileExist(is::GameConfig::CONFIG_FILE)) { #if defined(IS_ENGINE_VS) _mkdir #else mkdir #endif (is::GameConfig::DATA_PARENT_DIR.c_str() #if defined(SFML_SYSTEM_LINUX) || defined(IS_ENGINE_LINUX) , S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH #endif ); m_gameSysExt.saveConfig(is::GameConfig::CONFIG_FILE); } #endif #endif // defined setFPS(m_window, is::GameConfig::FPS); } #if defined(IS_ENGINE_HTML_5) void GameEngine::execMainLoop(std::function loop) { mainLoop = [myLoop = loop] {(void)myLoop();}; emscripten_set_main_loop(&MainLoop, -1, 1); } void GameEngine::execMainLoop(std::function loop) { mainLoop = loop; emscripten_set_main_loop(&MainLoop, -1, 1); } #endif bool GameEngine::play() { ////////////////////////////////////////////////////////////////////////////////////////////////////// // GAME INTILISATION ////////////////////////////////////////////////////////////////////////////////////////////////////// initEngine(); ////////////////////////////////////////////////////////////////////////////////////////////////////// // GAME STARTUP ////////////////////////////////////////////////////////////////////////////////////////////////////// std::unique_ptr app = nullptr; #if !defined(IS_ENGINE_HTML_5) while (m_window.isOpen() #ifdef __SWITCH__ && appletMainLoop() #endif ) #else EM_ASM(console.log("Start successfully!");, 0); execMainLoop([&] { if (emscripten_run_script_int("Module.syncdone") == 1) #endif { if (app == nullptr) app = std::make_unique(m_gameSysExt); else { app->update(); app->draw(); } } #if defined(IS_ENGINE_HTML_5) }); #endif return true; } } ================================================ FILE: app/src/main/cpp/GameFunction.cpp ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2024 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/system/function/GameFunction.h" #if defined(IS_ENGINE_HTML_5) #include #include // Allows to send C++ string vector in javascript code inline std::vector *vectorFromIntPointer(uintptr_t vec) { return reinterpret_cast *>(vec); } EMSCRIPTEN_BINDINGS(Wrappers) { emscripten::register_vector("VectorString").constructor(&vectorFromIntPointer, emscripten::allow_raw_pointers()); }; #endif namespace is { const float MAX_CLOCK_TIME = 0.018f; const float VALUE_CONVERSION = 65.f; const float SECOND = 59.f; const float VALUE_TIME = 1.538f; std::string w_chart_tToStr(wchar_t const *val) { std::wstring ws(val); return (std::string(ws.begin(), ws.end())); } std::wstring strToWStr(const std::string &str) { std::wstring wsTemp(str.begin(), str.end()); return wsTemp; } int getMSecond(const float &DELTA_TIME) { return static_cast(DELTA_TIME * (VALUE_TIME * VALUE_CONVERSION)); } std::tm makeTime(int year, int month, int day) { std::tm tm = {0}; tm.tm_year = year - 1900; // years count from 1900 tm.tm_mon = month - 1; // months count from January=0 tm.tm_mday = day; // days count from 1 return tm; } bool checkDateLimit(int year, int mont, int day) { time_t currentTime = time(0); std::tm tm1 = makeTime(year, mont, day); std::time_t expirationDate = std::mktime(&tm1); const int seconds_per_day = 60 * 60 * 24; return (std::difftime(expirationDate, currentTime) / seconds_per_day < 0.f); } void showLog(const std::string& str, bool stopApplication) { #if defined(IS_ENGINE_USE_SHOWLOG) #if !defined(__ANDROID__) std::cout << str.c_str() << "\n"; #else __android_log_print(ANDROID_LOG_DEBUG, "LOG_INFO", "%s\n", str.c_str()); #endif #endif if (stopApplication) is::closeApplication(); } bool isIn(unsigned short valNumber, const int var, int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8, int x9) { if (var == x1) return true; else if (var == x2) return true; else if (var == x3) return (valNumber > 2); else if (var == x4) return (valNumber > 3); else if (var == x5) return (valNumber > 4); else if (var == x6) return (valNumber > 5); else if (var == x7) return (valNumber > 6); else if (var == x8) return (valNumber > 7); else if (var == x9) return (valNumber > 8); return false; } bool isBetween(float a, float b, float c) { if (b <= c) return (b <= a && a <= c); else return (c <= a && a <= b); } int sign(float x) { if (x > 0.f) return 1; else if (x < 0.f) return -1; else return 0; } float pointDirection(float x1, float y1, float x2, float y2) { return atan((y1 - y2) / (x1 - x2)); } float radToDeg(float x) { return static_cast((x * 180.f) / 3.14159235f); } float degToRad(float x) { return static_cast((x * 3.14159235f) / 180.f); } float lengthDirX(float dir, float angle, bool useScreenScale) { #if defined(IS_ENGINE_SDL_2) if (useScreenScale) { if (static_cast(is::IS_ENGINE_SDL_screenXScale) != 1) { float tempDir(dir); dir += tempDir / is::IS_ENGINE_SDL_screenXScale; } return (dir * std::cos(degToRad(angle))) / is::IS_ENGINE_SDL_screenXScale; } #endif return dir * std::cos(degToRad(angle)); } float lengthDirY(float dir, float angle, bool useScreenScale) { #if defined(IS_ENGINE_SDL_2) if (useScreenScale) { if (static_cast(is::IS_ENGINE_SDL_screenXScale) != 1) { float tempDir(dir); dir += tempDir / is::IS_ENGINE_SDL_screenXScale; return (dir * std::sin(degToRad(angle))) / is::IS_ENGINE_SDL_screenYScale; } } #endif return dir * std::sin(degToRad(angle)); } bool collisionTest(Rectangle const &a, Rectangle const &b) { if (a.m_bottom <= b.m_top) return false; if (a.m_top >= b.m_bottom) return false; if (a.m_right <= b.m_left) return false; if (a.m_left >= b.m_right) return false; return true; } bool collisionTest(Circle const &a, Circle const &b) { auto distanceSquared = [](int x1, int y1, int x2, int y2) { int deltaX = x2 - x1; int deltaY = y2 - y1; return deltaX * deltaX + deltaY * deltaY; }; // Calculate total radius squared int totalRadiusSquared = a.m_raduis + b.m_raduis; totalRadiusSquared = totalRadiusSquared * totalRadiusSquared; // If the distance between the centers of the circles is less than the sum of their radii if (distanceSquared(a.m_x, a.m_y, b.m_x, b.m_y) < totalRadiusSquared) return true; // The circles have collided return false; // If not } bool collisionTest(Circle const &circle, Rectangle const &rec) { // temporary variables to set edges for testing float testX = circle.m_x; float testY = circle.m_y; // which edge is closest? if (circle.m_x < rec.m_left) testX = rec.m_left; // test left edge else if (circle.m_x > rec.m_right) testX = rec.m_right; // right edge if (circle.m_y < rec.m_top) testY = rec.m_top; // top edge else if (circle.m_y > rec.m_bottom) testY = rec.m_bottom; // bottom edge // get distance from closest edges float distX = circle.m_x - testX; float distY = circle.m_y - testY; float distance = sqrt((distX * distX) + (distY * distY)); // if the distance is less than the radius, collision! if (distance <= circle.m_raduis) return true; return false; } bool collisionTest(Rectangle const &rec, Circle const &circle) { return collisionTest(circle, rec); } void setTextAnimation(sf::Text &txt, sf::Sprite &spr, sf::Sprite &sprSelected, int &var, int val) { if (var == val) { is::setSFMLObjX_Y(sprSelected, is::getSFMLObjX(spr), is::getSFMLObjY(spr)); is::setSFMLObjFillColor(txt, is::GameConfig::DEFAULT_SFML_SELECTED_TEXT_COLOR); } else is::setSFMLObjFillColor(txt, is::GameConfig::DEFAULT_SFML_TEXT_COLOR); } void setTextAnimation(sf::Text &txt, int &var, int val) { if (var == val) is::setSFMLObjFillColor(txt, is::GameConfig::DEFAULT_SFML_SELECTED_TEXT_COLOR); else is::setSFMLObjFillColor(txt, is::GameConfig::DEFAULT_SFML_TEXT_COLOR); } void setFrame(sf::Sprite &sprite, float frame, int subFrame, int frameWidth, int frameHeight, int recWidth, int recHeight) { /* Description of the image decoupage algorithm * be << frame >> number of the image to get (to have it start counting images from 0 to X) * either << subFrame >> number of images on one line * be << frameLineIndex >> number of the line corresponding to the image * (frame / subFrame) returns the number of times there is << frame >> in << subFrame >> to determine the number of the line of the image * 32 * (frame - (subFrame * frameLineIndex)) returns the position (number) of the image on the X axis can return the value 0 (1st image on the line) * when the value of << frame >> exceeds the number of image on the X axis (the value of << subFrame >>) then we are on a new line * (32 * frameLineIndex) gives the position of the image on the Y axis its value varies according to the << frame >> * example: 32 * frameLineIndex = 1 when frame > subFrame; 32 * frameLineIndex = 2 when frame > subFrame * 2; ... */ int frameLineIndex = (frame / subFrame); setSFMLObjTexRec(sprite, frameWidth * (static_cast(frame) - (subFrame * frameLineIndex)), frameHeight * frameLineIndex, recWidth, recHeight); } void setFrame(sf::Sprite &sprite, float frame, int subFrame, int frameSize) { setFrame(sprite, frame, subFrame, frameSize, frameSize, frameSize, frameSize); } /* void createRenderTexture(sf::RenderTexture &renderTexture, unsigned int width, unsigned int height) { renderTexture.create(width, height); }*/ void createRectangle(sf::RectangleShape &rec, sf::Vector2f recSize, sf::Color color, float x, float y, bool center) { rec.setSize(recSize); if (center) is::centerSFMLObj(rec); setSFMLObjFillColor(rec, color); is::setSFMLObjX_Y(rec, x, y); } void textStyleConfig(sf::Text &txt, bool underLined, bool boldText, bool italicText) { if (underLined && boldText && italicText) txt.setStyle(sf::Text::Underlined | sf::Text::Bold | sf::Text::Italic); else if (underLined && boldText) txt.setStyle(sf::Text::Underlined | sf::Text::Bold); else if (underLined && italicText) txt.setStyle(sf::Text::Underlined | sf::Text::Italic); else if (boldText && italicText) txt.setStyle(sf::Text::Bold | sf::Text::Italic); else if (underLined) txt.setStyle(sf::Text::Underlined); else if (boldText) txt.setStyle(sf::Text::Bold); else if (italicText) txt.setStyle(sf::Text::Italic); } void setSFMLTextOutlineColor(sf::Text &txt, float thickness, sf::Color color) { txt.setOutlineColor(color); txt.setOutlineThickness(thickness); } void createSprite(sf::Texture &tex, sf::Sprite &spr, sf::Vector2f position, sf::Vector2f origin, bool smooth) { #if defined(IS_ENGINE_SFML) tex.setSmooth(smooth); #endif spr.setTexture(tex); spr.setOrigin(origin.x, origin.y); is::setSFMLObjX_Y(spr, position.x, position.y); } void createSprite(sf::Texture &tex, sf::Sprite &spr, sf::IntRect rec, sf::Vector2f position, sf::Vector2f origin, bool repeatTexture, bool smooth) { createSprite(tex, spr, position, origin, smooth); is::setSFMLObjTexRec(spr, rec.left, rec.top, rec.width, rec.height); #if defined(IS_ENGINE_SFML) if (repeatTexture) tex.setRepeated(true); #endif } void createSprite(sf::Texture &tex, sf::Sprite &spr, sf::IntRect rec, sf::Vector2f position, sf::Vector2f origin, sf::Vector2f scale, unsigned int alpha, bool repeatTexture, bool smooth) { createSprite(tex, spr, rec, position, origin, repeatTexture, smooth); is::setSFMLObjScaleX_Y(spr, scale.x, scale.y); is::setSFMLObjAlpha(spr, alpha); } sf::Vector2f getCursor(sf::RenderWindow &window, unsigned int finger) { sf::Vector2i pixelPos = ((IS_ENGINE_MOBILE_OS) ? sf::Touch::getPosition(finger, window) : sf::Mouse::getPosition(window) ); sf::Vector2f worldPos = window.mapPixelToCoords(pixelPos, window.getView()); float dx = pointDistance(window.getView().getCenter().x, window.getView().getCenter().y, worldPos.x, window.getView().getCenter().y); float dy = pointDistance(window.getView().getCenter().x, window.getView().getCenter().y, window.getView().getCenter().x, worldPos.y); if (worldPos.x < window.getView().getCenter().x) dx *= -1; if (worldPos.y < window.getView().getCenter().y) dy *= -1; return sf::Vector2f(window.getView().getCenter().x + dx, window.getView().getCenter().y + dy); } short vibrate(short duration) { #if defined(__ANDROID__) JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv(); jobject activity = (jobject)SDL_AndroidGetActivity(); jclass clazz(env->GetObjectClass(activity)); JavaVM* vm; env->GetJavaVM(&vm); // First, attach this thread to the main thread JavaVMAttachArgs attachargs; attachargs.version = JNI_VERSION_1_6; attachargs.name = "NativeThread"; attachargs.group = NULL; jint res = vm->AttachCurrentThread(&env, &attachargs); if (res == JNI_ERR) return EXIT_FAILURE; // Retrieve class information jclass natact = env->FindClass("android/app/NativeActivity"); jclass context = env->FindClass("android/content/Context"); // Get the value of a constant jfieldID fid = env->GetStaticFieldID(context, "VIBRATOR_SERVICE", "Ljava/lang/String;"); jobject svcstr = env->GetStaticObjectField(context, fid); // Get the method 'getSystemService' and call it jmethodID getss = env->GetMethodID(natact, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"); jobject vib_obj = env->CallObjectMethod(activity, getss, svcstr); // Get the object's class and retrieve the member name jclass vib_cls = env->GetObjectClass(vib_obj); jmethodID vibrate = env->GetMethodID(vib_cls, "vibrate", "(J)V"); // Determine the timeframe jlong length = duration; // Bzzz! env->CallVoidMethod(vib_obj, vibrate, length); // Free references env->DeleteLocalRef(vib_obj); env->DeleteLocalRef(vib_cls); env->DeleteLocalRef(svcstr); env->DeleteLocalRef(context); env->DeleteLocalRef(natact); env->DeleteLocalRef(clazz); // Detach thread again // this line is comment because it cause a bug // vm->DetachCurrentThread(); #elif defined(IS_ENGINE_HTML_5) EM_ASM_ARGS({ navigator.vibrate($0); }, duration); #else is::showLog("Vibrate Called ! Time : " + is::numToStr(duration) + " ms"); #endif return 1; // EXIT_SUCCESS; } void openURL(const std::string& url, OpenURLAction action) { std::string urlStr; switch(action) { case OpenURLAction::Http: urlStr = "http://" + url; break; case OpenURLAction::Tel: urlStr = "tel:" + url; break; default: urlStr = "mailto:" + url; break; } #if defined(__ANDROID__) JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv(); jobject activity = (jobject)SDL_AndroidGetActivity(); JavaVM* vm; env->GetJavaVM(&vm); vm->AttachCurrentThread(&env, NULL); // Retrieve class information jclass activityClass = env->FindClass("android/app/Activity"); jclass intentClass = env->FindClass("android/content/Intent"); jclass uriClass = env->FindClass("android/net/Uri"); // convert URL std::string to jstring jstring uriString = env->NewStringUTF(urlStr.c_str()); // call parse method jmethodID uriParse = env->GetStaticMethodID(uriClass, "parse", "(Ljava/lang/String;)Landroid/net/Uri;"); // set URL in method jobject uri = env->CallStaticObjectMethod(uriClass, uriParse, uriString); // intent action jstring actionString = env->NewStringUTF((action != OpenURLAction::Tel) ? "android.intent.action.VIEW" : "android.intent.action.DIAL"); // call the intent object constructor jmethodID newIntent = env->GetMethodID(intentClass, "", "(Ljava/lang/String;Landroid/net/Uri;)V"); // create the intent instance jobject intent = env->AllocObject(intentClass); // set intent constructor env->CallVoidMethod(intent, newIntent, actionString, uri); jmethodID startActivity = env->GetMethodID(activityClass, "startActivity", "(Landroid/content/Intent;)V"); env->CallVoidMethod(activity, startActivity, intent); env->DeleteLocalRef(activityClass); env->DeleteLocalRef(intentClass); env->DeleteLocalRef(uriClass); env->DeleteLocalRef(intent); env->DeleteLocalRef(activity); //vm->DetachCurrentThread(); #elif defined(IS_ENGINE_HTML_5) std::vector vectorArray; vectorArray.push_back(urlStr); EM_ASM_ARGS ({ var vectorArray = new Module.VectorString($0); window.open(vectorArray.get(0)); }, &vectorArray); #else std::string op = #if !defined(SFML_SYSTEM_LINUX) std::string("start ") #else std::string("xdg-open ") #endif .append(urlStr); system(op.c_str()); #endif } #if defined(__ANDROID__) std::string jstring2string(JNIEnv *env, jstring jStr) { if (!jStr) return ""; const jclass stringClass = env->GetObjectClass(jStr); const jmethodID getBytes = env->GetMethodID(stringClass, "getBytes", "(Ljava/lang/String;)[B"); const jbyteArray stringJbytes = (jbyteArray) env->CallObjectMethod(jStr, getBytes, env->NewStringUTF("UTF-8")); size_t length = (size_t) env->GetArrayLength(stringJbytes); jbyte* pBytes = env->GetByteArrayElements(stringJbytes, NULL); std::string ret = std::string((char *)pBytes, length); env->ReleaseByteArrayElements(stringJbytes, pBytes, JNI_ABORT); env->DeleteLocalRef(stringJbytes); env->DeleteLocalRef(stringClass); return ret; } std::string getDeviceId(JNIEnv *env, ANativeActivity *activity) { jclass classActivity = env->FindClass("android/app/NativeActivity"); jclass context = env->FindClass("android/content/Context"); jclass telephony = env->FindClass("android/telephony/TelephonyManager"); jfieldID field = env->GetStaticFieldID(context, "TELEPHONY_SERVICE", "Ljava/lang/String;"); jobject staticField = env->GetStaticObjectField(context, field); jmethodID getSS = env->GetMethodID(classActivity, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"); jobject objTel = env->CallObjectMethod(activity->clazz, getSS, staticField); jmethodID getId = env->GetMethodID(telephony, "getDeviceId", "()Ljava/lang/String;"); jstring strId = (jstring)env->CallObjectMethod(objTel, getId); env->DeleteLocalRef(classActivity); env->DeleteLocalRef(context); env->DeleteLocalRef(telephony); return jstring2string(env, strId); } #endif } ================================================ FILE: app/src/main/cpp/GameKeyData.cpp ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2024 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/system/function/GameKeyData.h" namespace is { GameKeyData::GameKeyData(is::GameDisplay *scene) : MainObject(), m_keyPausePressed(false), m_keyLeftPressed(false), m_keyRightPressed(false), m_keyUpPressed(false), m_keyDownPressed(false), m_keyAPressed(false), m_keyBPressed(false), m_keyAUsed(false), m_keyBUsed(false), m_keyLeftUsed(false), m_keyRightUsed(false), m_keyUpUsed(false), m_keyDownUsed(false), m_disableAllKey(false), m_hideGamePad(false), #if defined(__ANDROID__) m_moveObj(0.f), #endif m_scene(scene) { m_strName = "GameKeyData"; m_moveKeyPressed = V_KEY_NONE; m_actionKeyPressed = V_KEY_NONE; m_keyboardA = &is::GameConfig::KEY_A; m_keyboardB = &is::GameConfig::KEY_B; m_keyboardLeft = &is::GameConfig::KEY_LEFT; m_keyboardRight = &is::GameConfig::KEY_RIGHT; m_keyboardUp = &is::GameConfig::KEY_UP; m_keyboardDown = &is::GameConfig::KEY_DOWN; loadResources(); } void GameKeyData::loadResources(bool usePadColorBlack) { auto &tex = m_scene->GRMaddTexture("game_pad", is::GameConfig::GUI_DIR + "game_pad.png"); is::createSprite(tex, m_sprJoystick[0], sf::IntRect(0, (!usePadColorBlack) ? 0 : 134, 134, 134), sf::Vector2f(0.f, 0.f), sf::Vector2f(67.f, 67.f)); is::createSprite(tex, m_sprJoystick[1], sf::IntRect(134, ((!m_scene->getGameSystem().m_permutePadAB) ? 0 : 67) + ((!usePadColorBlack) ? 0 : 134), 144, 67), sf::Vector2f(0.f, 0.f), sf::Vector2f(72.f, 37.f)); is::setSFMLObjSize(m_recJoystickMask[0], 134.f, 134.f); is::setSFMLObjSize(m_recJoystickMask[1], 144.f, 74.f); for (int i(0); i < 2; i++) { is::setSFMLObjFillColor(m_recJoystickMask[i], sf::Color::Blue); is::centerSFMLObj(m_recJoystickMask[i]); } const float OBJ_SIZE(40.f), _ADD_SIZE(24.f), _ADD_ACT_SIZE(24.f), _ADD_W(16.f); is::setSFMLObjSize(m_recKeyLeftMask, OBJ_SIZE + _ADD_W, OBJ_SIZE + _ADD_SIZE); is::setSFMLObjSize(m_recKeyRightMask, OBJ_SIZE + _ADD_W, OBJ_SIZE + _ADD_SIZE); is::setSFMLObjSize(m_recKeyUpMask, OBJ_SIZE + _ADD_SIZE, OBJ_SIZE + _ADD_W); is::setSFMLObjSize(m_recKeyDownMask, OBJ_SIZE + _ADD_SIZE, OBJ_SIZE + _ADD_W); is::setSFMLObjSize(m_recKeyAMask, OBJ_SIZE + _ADD_ACT_SIZE, OBJ_SIZE + _ADD_ACT_SIZE); is::setSFMLObjSize(m_recKeyBMask, OBJ_SIZE + _ADD_ACT_SIZE, OBJ_SIZE + _ADD_ACT_SIZE); is::centerSFMLObj(m_recKeyLeftMask); is::centerSFMLObj(m_recKeyRightMask); is::centerSFMLObj(m_recKeyUpMask); is::centerSFMLObj(m_recKeyDownMask); is::centerSFMLObj(m_recKeyAMask); is::centerSFMLObj(m_recKeyBMask); } void GameKeyData::step(const float &DELTA_TIME) { if (!keyAPressed()) m_keyAUsed = false; if (!keyBPressed()) m_keyBUsed = false; if (!keyLeftPressed()) m_keyLeftUsed = false; if (!keyRightPressed()) m_keyRightUsed = false; if (!keyUpPressed()) m_keyUpUsed = false; if (!keyDownPressed()) m_keyDownUsed = false; m_keyLeftPressed = keyLeftPressed(); m_keyRightPressed = keyRightPressed(); m_keyUpPressed = keyUpPressed(); m_keyDownPressed = keyDownPressed(); m_keyAPressed = keyAPressed(); m_keyBPressed = keyBPressed(); if (m_keyLeftPressed) { m_keyRightPressed = false; m_keyUpPressed = false; m_keyDownPressed = false; if (!IS_ENGINE_MOBILE_OS) m_moveKeyPressed = V_KEY_LEFT; } else if (m_keyRightPressed) { m_keyLeftPressed = false; m_keyUpPressed = false; m_keyDownPressed = false; if (!IS_ENGINE_MOBILE_OS) m_moveKeyPressed = V_KEY_RIGHT; } else if (m_keyUpPressed) { m_keyLeftPressed = false; m_keyRightPressed = false; m_keyDownPressed = false; if (!IS_ENGINE_MOBILE_OS) m_moveKeyPressed = V_KEY_UP; } else if (m_keyDownPressed) { m_keyLeftPressed = false; m_keyRightPressed = false; m_keyUpPressed = false; if (!IS_ENGINE_MOBILE_OS) m_moveKeyPressed = V_KEY_DOWN; } if (is::IS_ENGINE_MOBILE_OS) { if (m_hideGamePad) { if (m_moveObj < 320.f) m_moveObj += (10.f * is::VALUE_CONVERSION) * DELTA_TIME; } else { is::decreaseVar(DELTA_TIME, m_moveObj, 8.f, 0.f, 10.f); } float moveMaskOnX(0.f), _LIMIT(-19.f), _POS(37.f); const float _W_SIZE(48.f); if (m_moveKeyPressed == V_KEY_LEFT) moveMaskOnX = -6.f; if (m_moveKeyPressed == V_KEY_RIGHT) moveMaskOnX = 6.f; is::setSFMLObjX_Y(m_sprJoystick[0], m_scene->getViewX() + m_scene->getGameSystem().m_padDirXPos, m_scene->getViewY() + m_scene->getGameSystem().m_padDirYPos + m_moveObj); is::setSFMLObjX_Y(m_sprJoystick[1], m_scene->getViewX() + m_scene->getGameSystem().m_padActionXPos, m_scene->getViewY() + m_scene->getGameSystem().m_padActionYPos + m_moveObj); is::setSFMLObjX_Y(m_recKeyLeftMask, (is::getSFMLObjX(m_sprJoystick[0]) - _W_SIZE / 2.f) + _LIMIT, is::getSFMLObjY(m_sprJoystick[0])); is::setSFMLObjX_Y(m_recKeyRightMask, (is::getSFMLObjX(m_sprJoystick[0]) + _W_SIZE / 2.f) - _LIMIT, is::getSFMLObjY(m_sprJoystick[0])); is::setSFMLObjX_Y(m_recKeyUpMask, is::getSFMLObjX(m_sprJoystick[0]), (is::getSFMLObjY(m_sprJoystick[0]) - _W_SIZE / 2.f) + _LIMIT); is::setSFMLObjX_Y(m_recKeyDownMask, is::getSFMLObjX(m_sprJoystick[0]), (is::getSFMLObjY(m_sprJoystick[0]) + _W_SIZE / 2.f) - _LIMIT); is::setSFMLObjX_Y(m_recKeyAMask, is::getSFMLObjX(m_sprJoystick[1]) + (_POS * ((!m_scene->getGameSystem().m_permutePadAB) ? -1.f : 1.f)), is::getSFMLObjY(m_sprJoystick[1])); is::setSFMLObjX_Y(m_recKeyBMask, is::getSFMLObjX(m_sprJoystick[1]) + (_POS * ((!m_scene->getGameSystem().m_permutePadAB) ? 1.f : -1.f)), is::getSFMLObjY(m_sprJoystick[1])); for (int i(0); i < 2; i++) { is::setSFMLObjX_Y(m_recJoystickMask[i], is::getSFMLObjX(m_sprJoystick[i]) + ((i == 0) ? moveMaskOnX : 0.f), is::getSFMLObjY(m_sprJoystick[i])); is::setSFMLObjAlpha(m_sprJoystick[i], m_scene->getGameSystem().m_padAlpha); } } else { if (m_keyAPressed) m_actionKeyPressed = V_KEY_A; else if (m_keyBPressed) m_actionKeyPressed = V_KEY_B; else m_actionKeyPressed = V_KEY_NONE; if (!m_keyLeftPressed && !m_keyRightPressed && !m_keyUpPressed && !m_keyDownPressed) m_moveKeyPressed = V_KEY_NONE; } } void GameKeyData::draw(is::Render &surface) { if (is::IS_ENGINE_MOBILE_OS) { if (m_moveObj < 320.f) { /* * This displays the virtual key mask on the screen * for (int i(0); i < 2; i++) surface.draw(m_recJoystickMask[i]); surface.draw(m_recKeyLeftMask); surface.draw(m_recKeyRightMask); surface.draw(m_recKeyUpMask); surface.draw(m_recKeyDownMask); surface.draw(m_recKeyAMask); surface.draw(m_recKeyBMask); */ for (int i(0); i < 2; i++) { if (m_scene->getIsPlaying()) surface.draw(m_sprJoystick[i]); } } } } bool GameKeyData::checkKeyPressed(VirtualKeyIndex virtualKeyIndex, sf::Keyboard::Key *keyboardIndex) { if (m_disableAllKey) return false; ////////////////////////////////////////////////////////// // Mobile version code //if (is::IS_ENGINE_MOBILE_OS) //{ if (virtualKeyPressed(virtualKeyIndex)) return true; //} else //{ if (m_scene->getGameSystem().keyIsPressed(*keyboardIndex)) return true; //} ////////////////////////////////////////////////////////// return false; } bool GameKeyData::keyLeftPressed() { return checkKeyPressed(V_KEY_LEFT, m_keyboardLeft); } bool GameKeyData::keyRightPressed() { return checkKeyPressed(V_KEY_RIGHT, m_keyboardRight); } bool GameKeyData::keyUpPressed() { return checkKeyPressed(V_KEY_UP, m_keyboardUp); } bool GameKeyData::keyDownPressed() { return checkKeyPressed(V_KEY_DOWN, m_keyboardDown); } bool GameKeyData::keyAPressed() { return checkKeyPressed(V_KEY_A, m_keyboardA); } bool GameKeyData::keyBPressed() { return checkKeyPressed(V_KEY_B, m_keyboardB); } bool GameKeyData::virtualKeyPressed(VirtualKeyIndex virtualKeyIndex) { // Joystick controller bool mouseOnLJoystick(false); bool mouseOnRJoystick(false); if (virtualKeyIndex == V_KEY_LEFT || virtualKeyIndex == V_KEY_RIGHT || virtualKeyIndex == V_KEY_UP || virtualKeyIndex == V_KEY_DOWN) { // Check collision with left joystick if (m_scene->mouseCollision(m_recJoystickMask[0]) || (IS_ENGINE_MOBILE_OS && m_scene->mouseCollision(m_recJoystickMask[0], 1))) mouseOnLJoystick = true; if (mouseOnLJoystick) { if (m_moveKeyPressed != V_KEY_NONE && m_moveKeyPressed != virtualKeyIndex) { if (m_scene->mouseCollision(m_recKeyLeftMask) || (IS_ENGINE_MOBILE_OS && m_scene->mouseCollision(m_recKeyLeftMask, 1))) m_moveKeyPressed = V_KEY_LEFT; else if (m_scene->mouseCollision(m_recKeyRightMask) || (IS_ENGINE_MOBILE_OS && m_scene->mouseCollision(m_recKeyRightMask, 1))) m_moveKeyPressed = V_KEY_RIGHT; else if (m_scene->mouseCollision(m_recKeyUpMask) || (IS_ENGINE_MOBILE_OS && m_scene->mouseCollision(m_recKeyUpMask, 1))) m_moveKeyPressed = V_KEY_UP; else if (m_scene->mouseCollision(m_recKeyDownMask) || (IS_ENGINE_MOBILE_OS && m_scene->mouseCollision(m_recKeyDownMask, 1))) m_moveKeyPressed = V_KEY_DOWN; } switch (m_moveKeyPressed) { case V_KEY_LEFT: return (m_moveKeyPressed == virtualKeyIndex); break; case V_KEY_RIGHT: return (m_moveKeyPressed == virtualKeyIndex); break; case V_KEY_UP: return (m_moveKeyPressed == virtualKeyIndex); break; case V_KEY_DOWN: return (m_moveKeyPressed == virtualKeyIndex); break; default: break; } } // If left joystick pressed if ((m_scene->getGameSystem().isPressed() || (IS_ENGINE_MOBILE_OS && m_scene->getGameSystem().isPressed(1))) && mouseOnLJoystick) { switch (virtualKeyIndex) { case V_KEY_LEFT: if (m_scene->mouseCollision(m_recKeyLeftMask) || (IS_ENGINE_MOBILE_OS && m_scene->mouseCollision(m_recKeyLeftMask, 1))) { m_moveKeyPressed = V_KEY_LEFT; // is::showLog("L Pressed !"); return true; } break; case V_KEY_RIGHT: if (m_scene->mouseCollision(m_recKeyRightMask) || (IS_ENGINE_MOBILE_OS && m_scene->mouseCollision(m_recKeyRightMask, 1))) { m_moveKeyPressed = V_KEY_RIGHT; // is::showLog("R Pressed !"); return true; } break; case V_KEY_UP: if (m_scene->mouseCollision(m_recKeyUpMask) || (IS_ENGINE_MOBILE_OS && m_scene->mouseCollision(m_recKeyUpMask, 1))) { m_moveKeyPressed = V_KEY_UP; // is::showLog("U Pressed !"); return true; } break; case V_KEY_DOWN: if (m_scene->mouseCollision(m_recKeyDownMask) || (IS_ENGINE_MOBILE_OS && m_scene->mouseCollision(m_recKeyDownMask, 1))) { m_moveKeyPressed = V_KEY_DOWN; // is::showLog("D Pressed !"); return true; } break; default: break; } } m_moveKeyPressed = V_KEY_NONE; } if (virtualKeyIndex == V_KEY_A || virtualKeyIndex == V_KEY_B) { // Check collision with right joystick if (m_scene->mouseCollision(m_recJoystickMask[1]) || (IS_ENGINE_MOBILE_OS && m_scene->mouseCollision(m_recJoystickMask[1], 1))) mouseOnRJoystick = true; // If left joystick pressed if ((m_scene->getGameSystem().isPressed() || (IS_ENGINE_MOBILE_OS && m_scene->getGameSystem().isPressed(1))) && mouseOnRJoystick) { switch (virtualKeyIndex) { case V_KEY_A: if (m_scene->mouseCollision(m_recKeyAMask) || (IS_ENGINE_MOBILE_OS && m_scene->mouseCollision(m_recKeyAMask, 1))) { m_actionKeyPressed = V_KEY_A; // is::showLog("A Pressed !"); return true; } break; case V_KEY_B: if (m_scene->mouseCollision(m_recKeyBMask) || (IS_ENGINE_MOBILE_OS && m_scene->mouseCollision(m_recKeyBMask, 1))) { m_actionKeyPressed = V_KEY_B; // is::showLog("B Pressed !"); return true; } break; default: break; } } m_actionKeyPressed = V_KEY_NONE; } return false; } }; ================================================ FILE: app/src/main/cpp/GameSlider.cpp ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2024 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/system/function/GameSlider.h" namespace is { GameSlider::GameSlider(is::GameDisplay *scene): MainObject(), Type(SLIDE_NONE), m_scene(scene), m_slideDistance(64.f) { m_strName = "GameSlider"; #if defined(IS_ENGINE_USE_SDM) m_depth = 999999999; // will update this object before all others #endif } void GameSlider::step(const float &DELTA_TIME) { if (m_scene->getGameSystem().isPressed(is::GameSystem::MOUSE)) { sf::Vector2f cursor(m_scene->getCursor()); if (static_cast(m_time) == 0) { m_xStart = cursor.x; m_yStart = cursor.y; } m_time = 5.f; m_x = cursor.x; m_y = cursor.y; if (m_type == SLIDE_NONE) { if (m_x > m_xStart + m_slideDistance) m_type = SLIDE_RIGHT; else if (m_x < m_xStart - m_slideDistance) m_type = SLIDE_LEFT; else if (m_y < m_yStart - m_slideDistance) m_type = SLIDE_UP; else if (m_y > m_yStart + m_slideDistance) m_type = SLIDE_DOWN; } } if (m_time > 0.f) m_time -= is::getMSecond(DELTA_TIME); else { m_type = SLIDE_NONE; m_time = 0.f; } } GameSlider::SlideDirection GameSlider::getSlideDirection() const { switch (m_type) { default: break; case 1: return SLIDE_UP; break; case 2: return SLIDE_DOWN; break; case 3: return SLIDE_RIGHT; break; case 4: return SLIDE_LEFT; break; } return SLIDE_NONE; } } ================================================ FILE: app/src/main/cpp/GameSystem.cpp ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2024 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/system/function/GameSystem.h" namespace is { const short MOUSE = 0; const short KEYBOARD = -1; const short ALL_BUTTONS = -2; GameSystem::GameSystem(sf::RenderWindow &window): m_window(window) { srand((unsigned int)time(0)); m_gameLanguage = 0; // 0 = default language m_validationMouseKey = &GameConfig::KEY_VALIDATION_MOUSE; m_validationKeyboardKey = &GameConfig::KEY_VALIDATION_KEYBOARD; m_disableKey = false; m_enableSound = true; m_enableMusic = true; m_enableVibrate = true; m_loadParentResources = false; m_keyIsPressed = false; } bool GameSystem::isPressed(int finger) const { if (m_disableKey) return false; ////////////////////////////////////////////////////////// // Android version code if (is::IS_ENGINE_MOBILE_OS) { // Exclude the keyboard test when we are on Android if (finger == -1) return false; // When testing the mouse validation buttons on PC then consider it as a touch if (finger == -2) finger = 0; if (sf::Touch::isDown(finger)) return true; } else { ////////////////////////////////////////////////////////// switch (finger) { case MOUSE: if (sf::Mouse::isButtonPressed(*m_validationMouseKey)) return true; break; case KEYBOARD: if (sf::Keyboard::isKeyPressed(*m_validationKeyboardKey)) return true; break; case ALL_BUTTONS: if (sf::Mouse::isButtonPressed(*m_validationMouseKey)) return true; else if (sf::Keyboard::isKeyPressed(*m_validationKeyboardKey)) return true; break; } } return false; } bool GameSystem::isPressed(ValidationButton validationButton) const { int value = validationButton; return isPressed(value); } bool GameSystem::keyIsPressed(sf::Keyboard::Key key) const { if (m_disableKey) return false; #ifdef __SWITCH__ const Uint8 *state = getControllerButtonState(NULL); if (key == is::GameConfig::KEY_VALIDATION_KEYBOARD) {if (state[SDL_CONTROLLER_BUTTON_START]) return true;} if (key == is::GameConfig::KEY_CANCEL) {if (state[SDL_CONTROLLER_BUTTON_BACK]) return true;} if (key == is::GameConfig::KEY_A) {if (state[SDL_CONTROLLER_BUTTON_A]) return true;} if (key == is::GameConfig::KEY_B) {if (state[SDL_CONTROLLER_BUTTON_B]) return true;} if (key == is::GameConfig::KEY_X) {if (state[SDL_CONTROLLER_BUTTON_X]) return true;} if (key == is::GameConfig::KEY_Y) {if (state[SDL_CONTROLLER_BUTTON_Y]) return true;} if (key == is::GameConfig::KEY_LEFT) {if (state[SDL_CONTROLLER_BUTTON_DPAD_LEFT]) return true;} if (key == is::GameConfig::KEY_RIGHT) {if (state[SDL_CONTROLLER_BUTTON_DPAD_RIGHT]) return true;} if (key == is::GameConfig::KEY_UP) {if (state[SDL_CONTROLLER_BUTTON_DPAD_UP]) return true;} if (key == is::GameConfig::KEY_DOWN) {if (state[SDL_CONTROLLER_BUTTON_DPAD_DOWN]) return true;} return false; #endif if (sf::Keyboard::isKeyPressed(key)) return true; return false; } bool GameSystem::keyIsPressed(sf::Mouse::Button button) const { if (m_disableKey) return false; if (sf::Mouse::isButtonPressed(button)) return true; return false; } bool GameSystem::fileExist(const std::string &fileName) { return is::fileExist(fileName); } void GameSystem::removeFile(const std::string &fileName) { remove(fileName.c_str()); #if defined(IS_ENGINE_HTML_5) EM_ASM(FS.syncfs(false, function(err){console.log(err)});, 0); #endif } void GameSystem::playSound(sf::Sound &obj) { if (m_enableSound) is::playSFMLSnd(obj); } void GameSystem::playSound(sf::Sound *obj) { if (m_enableSound) is::playSFMLSnd(obj); } void GameSystem::playMusic(sf::Music &obj) { if (m_enableMusic) is::playSFMLSnd(obj); } void GameSystem::playMusic(sf::Music *obj) { if (m_enableMusic) is::playSFMLSnd(obj); } void GameSystem::stopSound(sf::Sound &obj) { if (m_enableSound) { if (is::checkSFMLSndState(obj, SFMLSndStatus::Playing)) is::stopSFMLSnd(obj); } } void GameSystem::stopMusic(sf::Music &obj) { if (m_enableMusic) { if (is::checkSFMLSndState(obj, SFMLSndStatus::Playing)) is::stopSFMLSnd(obj); } } void GameSystem::useVibrate(short ms) { if (m_enableVibrate) is::vibrate(ms); } void GameSystem::saveConfig(const std::string &fileName) { FILE *file = NULL; //file = fopen(fileName.c_str(), "wb"); if (file != NULL) { fwrite(&m_enableSound, sizeof(bool), 1, file); fwrite(&m_enableMusic, sizeof(bool), 1, file); fwrite(&m_enableVibrate, sizeof(bool), 1, file); fwrite(&m_gameLanguage, sizeof(int), 1, file); fwrite(&m_firstLaunch, sizeof(bool), 1, file); fclose(file); #if defined(IS_ENGINE_HTML_5) EM_ASM(FS.syncfs(false, function(err){console.log(err)});, 0); #endif } } void GameSystem::loadConfig(const std::string &fileName) { FILE *file = NULL; //file = fopen(fileName.c_str(), "rb"); if (file != NULL) { fread(&m_enableSound, sizeof(bool), 1, file); fread(&m_enableMusic, sizeof(bool), 1, file); fread(&m_enableVibrate, sizeof(bool), 1, file); fread(&m_gameLanguage, sizeof(int), 1, file); fread(&m_firstLaunch, sizeof(bool), 1, file); fclose(file); } } void GameSystem::savePadConfig(const std::string &fileName) { FILE *file = NULL; //file = fopen(fileName.c_str(), "wb"); if (file != NULL) { fwrite(&m_padDirXPos, sizeof(float), 1, file); fwrite(&m_padDirYPos, sizeof(float), 1, file); fwrite(&m_padActionXPos, sizeof(float), 1, file); fwrite(&m_padActionYPos, sizeof(float), 1, file); fwrite(&m_padAlpha, sizeof(int), 1, file); fwrite(&m_permutePadAB, sizeof(bool), 1, file); fclose(file); #if defined(IS_ENGINE_HTML_5) EM_ASM(FS.syncfs(false, function(err){console.log(err)});, 0); #endif } } void GameSystem::loadPadConfig(const std::string &fileName) { FILE *file = NULL; //file = fopen(fileName.c_str(), "rb"); if (file != NULL) { fread(&m_padDirXPos, sizeof(float), 1, file); fread(&m_padDirYPos, sizeof(float), 1, file); fread(&m_padActionXPos, sizeof(float), 1, file); fread(&m_padActionYPos, sizeof(float), 1, file); fread(&m_padAlpha, sizeof(int), 1, file); fread(&m_permutePadAB, sizeof(bool), 1, file); fclose(file); } } void GSMplaySound(const std::string& name, std::vector> &GSMsound, GameSystem &gameSystem) { bool soundExist(false); WITH(GSMsound.size()) { if (GSMsound[_I].get() != nullptr) { if (GSMsound[_I]->getName() == name) { soundExist = true; if (GSMsound[_I]->getFileIsLoaded()) gameSystem.playSound(GSMsound[_I]->getSound()); else is::showLog("ERROR: Can't play <" + name + "> sound!"); break; } } } if (!soundExist) is::showLog("ERROR: Can't play <" + name + "> sound because sound does not exist!"); } void GSMplayMusic(const std::string& name, std::vector> &GSMmusic, GameSystem &gameSystem) { //#if defined(__ANDROID__) // GSMplaySound(name, GSMmusic, gameSystem); //#else bool musicExist(false); WITH(GSMmusic.size()) { if (GSMmusic[_I].get() != nullptr) { if (GSMmusic[_I]->getName() == name) { musicExist = true; if (GSMmusic[_I]->getFileIsLoaded()) gameSystem.playMusic(GSMmusic[_I]->getMusic()); else is::showLog("ERROR: Can't play <" + name + "> music!"); break; } } } if (!musicExist) is::showLog("ERROR: Can't play <" + name + "> music because music does not exist!"); //#endif } } ================================================ FILE: app/src/main/cpp/GameSystemExtended.cpp ================================================ #include "app_src/gamesystem_ext/GameSystemExtended.h" namespace is { GameSystemExtended::GameSystemExtended(sf::RenderWindow &window) : GameSystem(window) { initProgress(); // Default position of the Virtual Pad Game on the screen m_defaultPadDirXPos = -250.f; m_defaultPadDirYPos = 170.f; m_defaultPadActionXPos = 245.f; m_defaultPadActionYPos = 200.f; // Configurable position of the Virtual Pad Game on the screen m_padDirXPos = m_defaultPadDirXPos; m_padDirYPos = m_defaultPadDirYPos; m_padActionXPos = m_defaultPadActionXPos; m_padActionYPos = m_defaultPadActionYPos; m_permutePadAB = false; m_padAlpha = 255; } void GameSystemExtended::initProgress() { m_gameProgression = 0; // LEVEL 1 m_currentLives = 3; m_currentBonus = 0; m_currentLevel = 0; m_currentScore = 0; m_currentHiScore = 0; } void GameSystemExtended::initSystemData() { // global variable m_gameLanguage = 0; m_firstLaunch = true; // Determine the number of levels // LEVEL_MAX - 1 because we count the number of levels from zero (0) as for the array) m_levelNumber = is::level::LevelId::LEVEL_MAX - 1; initProgress(); } void GameSystemExtended::initData(bool clearCurrentLevel) { if (clearCurrentLevel) m_currentLevel = 0; // LEVEL 1 m_currentLives = 3; m_currentBonus = 0; m_currentScore = 0; } void GameSystemExtended::saveData(std::string const &fileName) { FILE *file = NULL; //file = fopen(fileName.c_str(), "wb"); if (file != NULL) { fwrite(&m_gameProgression, sizeof(int), 1, file); fwrite(&m_currentLives, sizeof(int), 1, file); fwrite(&m_currentBonus, sizeof(int), 1, file); fwrite(&m_currentHiScore, sizeof(int), 1, file); fclose(file); #if defined(IS_ENGINE_HTML_5) EM_ASM(FS.syncfs(false, function(err){console.log(err)});, 0); #endif } } void GameSystemExtended::loadData(std::string const &fileName) { FILE *file = NULL; //file = fopen(fileName.c_str(), "rb"); if (file != NULL) { fread(&m_gameProgression, sizeof(int), 1, file); fread(&m_currentLives, sizeof(int), 1, file); fread(&m_currentBonus, sizeof(int), 1, file); fread(&m_currentHiScore, sizeof(int), 1, file); fclose(file); } } } ================================================ FILE: app/src/main/cpp/GameTime.cpp ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2024 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/system/function/GameFunction.h" #include "isEngine/system/function/GameTime.h" namespace is { GameTime::GameTime(): m_minute(0), m_second(0), m_mSecond(0) { } GameTime::GameTime(unsigned int ms): m_minute(0), m_second(0), m_mSecond(0) { m_mSecond = ms; m_second += m_mSecond / 60; m_minute += m_second / 60; m_mSecond %= 60; m_second %= 60; } GameTime::GameTime(unsigned int m, unsigned int s, unsigned int ms): m_minute(m), m_second(s), m_mSecond(ms) { } GameTime::~GameTime() { } void GameTime::step(const float &DELTA_TIME) { if (m_mSecond > 0) m_mSecond -= static_cast((is::VALUE_CONVERSION * is::VALUE_TIME) * DELTA_TIME); else { if (m_second == 0) { if (m_minute > 0) { m_second = 59; m_mSecond = 59; m_minute--; } } else { m_second--; m_mSecond = 59; } } } void GameTime::addTimeValue(int m, int s, int ms) { m_minute += m; m_second += s; if (m_second > 59) { m_minute++; m_second = m_second - 59; } m_mSecond += ms; } void GameTime::setTimeValue(int m, int s, int ms) { m_minute = m; m_second = s; m_mSecond = ms; } void GameTime::setMSecond(int ms) { m_second = 0; m_minute = 0; m_mSecond = ms; m_second += m_mSecond / 60; m_minute += m_second / 60; m_mSecond %= 60; m_second %= 60; } GameTime& GameTime::operator=(GameTime const &t) { m_minute = t.m_minute; m_second = t.m_second; m_mSecond = t.m_mSecond; return *this; } unsigned int GameTime::getTimeValue() const { return ((m_minute * 3600) + (m_second * 60) + m_mSecond); } unsigned int GameTime::getMinute() const { return m_minute; } unsigned int GameTime::getSecond() const { return m_second; } unsigned int GameTime::getMSecond() const { return m_mSecond; } bool GameTime::compareTime(unsigned int m, unsigned int s, unsigned int ms) const { return (((m * 3600) + (s * 60) + ms) >= getTimeValue()); } const std::string GameTime::getTimeString() const noexcept { std::string str; str = writeZero(m_minute) + ":" + writeZero(m_second) + "." + writeZero(m_mSecond); return str; } bool operator==(GameTime const &t1, GameTime const &t2) { return (t1.getTimeValue() == t2.getTimeValue()); } bool operator>(GameTime const &t1, GameTime const &t2) { return (t1.getTimeValue() > t2.getTimeValue()); } bool operator<(GameTime const &t1, GameTime const &t2) { return (t1.getTimeValue() < t2.getTimeValue()); } std::ostream& operator<<(std::ostream &flux, GameTime const &t) { flux << t.m_minute << "m " << t.m_second << "s " << t.m_mSecond << "ms" << std::endl; return flux; } } ================================================ FILE: app/src/main/cpp/Main.hpp ================================================ #ifndef MAIN_HPP #define MAIN_HPP #ifdef __APPLE__ #include "vscode/MacOS/MacHelper.hpp" MacHelper macHelper; #endif // __APPLE__ #ifdef __linux__ #include "vscode/Linux/LinuxHelper.hpp" LinuxHelper linuxHelper; #endif // __linux__ #ifdef _WIN32 #include "vscode/Win32/WindowsHelper.hpp" WindowsHelper windowsHelper; #endif // _WIN32 // #endif // MAIN_HPP ================================================ FILE: app/src/main/cpp/MainObject.cpp ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2024 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/system/entity/MainObject.h" namespace is { int MainObject::instanceNumber = 0; MainObject::MainObject(): Name(), #if defined(IS_ENGINE_USE_SDM) Destructible(), DepthObject(DepthObject::NORMAL_DEPTH), Visibility(), #endif m_x(0.f), m_y(0.f), m_xStart(0.f), m_yStart(0.f), m_xPrevious(m_x), m_yPrevious(m_y), m_speed(0.f), m_hsp(0.f), m_vsp(0.f), m_frameStart(0.f), m_frameEnd(0.f), m_frame(0.f), m_imageXscale(1.f), m_imageYscale(1.f), m_imageScale(1.f), m_imageAngle(0.f), m_xOffset(0.f), m_yOffset(0.f), m_time(0.f), m_w(32), m_h(32), m_imageAlpha(255), m_imageIndex(0), m_isActive(false), m_isSDMSprite(false), m_drawMask(false) { updateCollisionMask(); instanceNumber++; m_instanceId = instanceNumber; } MainObject::MainObject(float x, float y): Name(), #if defined(IS_ENGINE_USE_SDM) Destructible(), DepthObject(DepthObject::NORMAL_DEPTH), Visibility(), #endif m_x(x), m_y(y), m_xStart(x), m_yStart(y), m_xPrevious(m_x), m_yPrevious(m_y), m_speed(0.f), m_hsp(0.f), m_vsp(0.f), m_frameStart(0.f), m_frameEnd(0.f), m_frame(0.f), m_imageXscale(1.f), m_imageYscale(1.f), m_imageScale(1.f), m_imageAngle(0.f), m_xOffset(0.f), m_yOffset(0.f), m_time(0.f), m_w(32), m_h(32), m_imageAlpha(255), m_imageIndex(0), m_isActive(false), m_isSDMSprite(false), m_drawMask(false) { updateCollisionMask(); instanceNumber++; m_instanceId = instanceNumber; } MainObject::MainObject(sf::Sprite &spr, float x, float y): Name(), #if defined(IS_ENGINE_USE_SDM) Destructible(), DepthObject(DepthObject::NORMAL_DEPTH), Visibility(), #endif m_x((static_cast(x) == 0) ? x : spr.getPosition().x), m_y((static_cast(y) == 0) ? y : spr.getPosition().y), m_xStart(m_x), m_yStart(m_y), m_xPrevious(m_x), m_yPrevious(m_y), m_speed(0.f), m_hsp(0.f), m_vsp(0.f), m_frameStart(0.f), m_frameEnd(0.f), m_frame(0.f), m_imageXscale(1.f), m_imageYscale(1.f), m_imageScale(1.f), m_imageAngle(0.f), m_xOffset(0.f), m_yOffset(0.f), m_time(0.f), m_w(32), m_h(32), m_imageAlpha(255), m_imageIndex(0), m_isActive(false), m_isSDMSprite(true), m_drawMask(false), m_sprParent(spr) { #if defined(IS_ENGINE_USE_SDM) m_SDMcallStep = false; m_SDMcallEvent = false; #endif setRectangleMask(spr.getTexture()->getSize().x, spr.getTexture()->getSize().y); updateCollisionMask(); updateSprite(); instanceNumber++; m_instanceId = instanceNumber; } MainObject::MainObject(sf::Texture &tex, float x, float y, bool center): Name(), #if defined(IS_ENGINE_USE_SDM) Destructible(), DepthObject(DepthObject::NORMAL_DEPTH), #endif m_x(x), m_y(y), m_xStart(m_x), m_yStart(m_y), m_xPrevious(m_x), m_yPrevious(m_y), m_speed(0.f), m_hsp(0.f), m_vsp(0.f), m_frameStart(0.f), m_frameEnd(0.f), m_frame(0.f), m_imageXscale(1.f), m_imageYscale(1.f), m_imageScale(1.f), m_imageAngle(0.f), m_xOffset(0.f), m_yOffset(0.f), m_time(0.f), m_w(32), m_h(32), m_imageAlpha(255), m_imageIndex(0), m_isActive(false), m_isSDMSprite(true), m_drawMask(false) { m_centerSpr = center; #if defined(IS_ENGINE_USE_SDM) m_SDMcallStep = false; m_SDMcallEvent = false; #endif setRectangleMask(tex.getSize().x, tex.getSize().y); is::createSprite(tex, m_sprParent, sf::Vector2f(m_x, m_y), sf::Vector2f(0.f, 0.f)); if (!m_centerSpr) updateCollisionMask(); else { is::centerSFMLObj(m_sprParent); centerCollisionMask(m_x, m_y); } updateSprite(); instanceNumber++; m_instanceId = instanceNumber; } MainObject::~MainObject() { instanceNumber--; } void MainObject::setPosition(float x, float y) { m_xPrevious = m_x; m_yPrevious = m_y; m_x = x; m_y = y; updateSDMsprite(); } void MainObject::setSpriteScale(float x, float y) { is::setSFMLObjScaleX_Y(m_sprParent, x, y); } void MainObject::setXStart(float x) { m_xStart = x; } void MainObject::setYStart(float y) { m_yStart = y; } void MainObject::setXPrevious(float x) { m_xPrevious = x; } void MainObject::setYPrevious(float y) { m_yPrevious = y; } void MainObject::setStartPosition(float x, float y) { m_xStart = x; m_yStart = y; } void MainObject::setX(float x) { setPosition(x, m_y); } void MainObject::setY(float y) { setPosition(m_x, y); } void MainObject::moveX(float x) { setPosition(m_x + x, m_y); } void MainObject::moveY(float y) { setPosition(m_x, m_y + y); } void MainObject::setSpeed(float val) { m_speed = val; } void MainObject::setHsp(float val) { m_hsp = val; } void MainObject::setVsp(float val) { m_vsp = val; } void MainObject::setAngularMove(const float &DELTA_TIME, float speed, float angle) { m_x += (is::lengthDirX(speed, angle) * is::VALUE_CONVERSION) * DELTA_TIME; m_y -= (is::lengthDirY(speed, angle) * is::VALUE_CONVERSION) * DELTA_TIME; updateSDMsprite(); } void MainObject::setFrame(float val) { m_frame = val; } void MainObject::setFrameStart(float val) { m_frameStart = val; } void MainObject::setFrameEnd(float val) { m_frameEnd = val; } void MainObject::setImageXscale(float val) { setImageScaleX_Y(val, m_imageYscale); } void MainObject::setImageYscale(float val) { setImageScaleX_Y(m_imageXscale, val); } void MainObject::setImageScale(float val, bool updateXYscale) { m_imageScale = val; if (updateXYscale) setImageScaleX_Y(m_imageScale, m_imageScale); } void MainObject::setImageAngle(float val) { m_imageAngle = val; updateSDMsprite(); } void MainObject::setXOffset(float val) { setXYOffset(val, m_xOffset); } void MainObject::setYOffset(float val) { setXYOffset(m_xOffset, val); } void MainObject::setXYOffset(float x, float y) { m_xOffset = x; m_yOffset = y; updateSDMsprite(); } void MainObject::setXYOffset() { setXYOffset(is::getSFMLObjOriginX(m_sprParent), is::getSFMLObjOriginY(m_sprParent)); } void MainObject::setImageScaleX_Y(float x, float y) { m_imageXscale = x; m_imageYscale = y; updateSDMsprite(); } void MainObject::setTime(float x) { m_time = x; } void MainObject::setImageAlpha(int val) { m_imageAlpha = val; updateSDMsprite(); } void MainObject::setImageIndex(int val) { m_imageIndex = val; } void MainObject::setMaskW(int val) { m_w = val; } void MainObject::setMaskH(int val) { m_h = val; } void MainObject::setRectangleMask(int width, int height) { m_w = width; m_h = height; m_circle.m_raduis = 0.f; updateCollisionMask(); } void MainObject::setCircleMask(float raduis) { m_circle.m_raduis = raduis; m_w = 0; m_h = 0; updateCollisionMask(); } void MainObject::setIsActive(bool val) { m_isActive = val; } void MainObject::updateCollisionMask() { if (m_w > 0 && m_h > 0) { m_aabb.m_left = static_cast(m_x); m_aabb.m_top = static_cast(m_y); m_aabb.m_right = static_cast(m_x) + m_w; m_aabb.m_bottom = static_cast(m_y) + m_h; } else {m_circle.m_x = m_x; m_circle.m_y = m_y;} } void MainObject::updateCollisionMask(int x, int y) { if (m_w > 0 && m_h > 0) { m_aabb.m_left = static_cast(x); m_aabb.m_top = static_cast(y); m_aabb.m_right = static_cast(x) + m_w; m_aabb.m_bottom = static_cast(y) + m_h; } else {m_circle.m_x = x; m_circle.m_y = y;} } void MainObject::centerCollisionMask(int x, int y) { if (m_w > 0 && m_h > 0) { m_aabb.m_left = static_cast(x - m_w / 2.f); m_aabb.m_top = static_cast(y - m_h / 2.f); m_aabb.m_right = static_cast(x + m_w / 2.f); m_aabb.m_bottom = static_cast(y + m_h / 2.f); } else {m_circle.m_x = x; m_circle.m_y = y;} } void MainObject::updateSprite() { is::setSFMLObjAlpha(m_sprParent, m_imageAlpha); is::setSFMLObjAngle(m_sprParent, m_imageAngle); is::setSFMLObjScaleX_Y(m_sprParent, m_imageXscale, m_imageYscale); is::setSFMLObjX_Y(m_sprParent, m_x + m_xOffset, m_y + m_yOffset); } void MainObject::updateSprite(float x, float y, float angle, int alpha, float xScale, float yScale, float xOffset, float yOffset) { is::setSFMLObjAlpha(m_sprParent, alpha); is::setSFMLObjAngle(m_sprParent, angle); is::setSFMLObjScaleX_Y(m_sprParent, xScale, yScale); is::setSFMLObjX_Y(m_sprParent, x + xOffset, y + yOffset); } void MainObject::draw(is::Render &surface) { is::draw(surface, m_sprParent); if (m_drawMask) drawMask(surface); } void MainObject::drawMask(is::Render &surface, sf::Color color) { // We draw the AABB (rectangle, square) mask only if it has dimensions if (m_w > 0 && m_h > 0) { sf::RectangleShape rec(sf::Vector2f(static_cast(m_w), static_cast(m_h))); rec.setOutlineThickness(1.f); rec.setFillColor(sf::Color::Transparent); rec.setOutlineColor(color); is::setSFMLObjX_Y(rec, static_cast(m_aabb.m_left), static_cast(m_aabb.m_top)); is::draw(surface, rec); } else if (m_circle.m_raduis > 0.f) // We draw the circle mask only if it has dimensions { sf::CircleShape circle(m_circle.m_raduis); circle.setOutlineThickness(1.f); circle.setFillColor(sf::Color::Transparent); circle.setOutlineColor(color); is::centerSFMLObj(circle); is::setSFMLObjX_Y(circle, m_circle.m_x, m_circle.m_y); is::draw(surface, circle); } } void MainObject::setFrameLimit(float frameStart, float frameEnd) { if (frameEnd > -1.f) { if (m_frame > frameEnd) m_frame = frameStart; if (m_frame < frameStart) m_frame = frameStart; } else m_frame = frameStart; } float MainObject::distantToPoint(float x, float y) const { float X = (getX() + getMaskW() / 2) - x; float Y = (getY() + getMaskH() / 2) - y; return sqrt(X * X + Y * Y); } float MainObject::distantToObject(MainObject const *other, bool useSpritePosition) const { float X = ((useSpritePosition) ? getSpriteX() - other->getSpriteX() : (getX() + (getMaskW() / 2)) - (other->getX() + (other->getMaskW() / 2))); float Y = ((useSpritePosition) ? getSpriteY() - other->getSpriteY() : (getY() + (getMaskH() / 2)) - (other->getY() + (other->getMaskH() / 2))); return sqrt(X * X + Y * Y); } float MainObject::distantToObject(std::shared_ptr const &other, bool useSpritePosition) const { float X = ((useSpritePosition) ? getSpriteX() - other->getSpriteX() : (getX() + (getMaskW() / 2)) - (other->getX() + (other->getMaskW() / 2))); float Y = ((useSpritePosition) ? getSpriteY() - other->getSpriteY() : (getY() + (getMaskH() / 2)) - (other->getY() + (other->getMaskH() / 2))); return sqrt(X * X + Y * Y); } float MainObject::pointDirection(float x, float y) const { return (is::pointDirection(m_x, m_y, x, y)); } float MainObject::pointDirection(std::shared_ptr const &other) const { return (is::pointDirection(m_x, m_y, other->getX(), other->getY())); } float MainObject::pointDirectionSprite(float x, float y) const { return (is::pointDirection(getSpriteX(), getSpriteY(), x, y)); } float MainObject::pointDirectionSprite(std::shared_ptr const &other) const { return (is::pointDirection(getSpriteX(), getSpriteY(), other->getSpriteX(), other->getSpriteY())); } float MainObject::getSpeed() const { return m_speed; } float MainObject::getHsp() const { return m_hsp; } float MainObject::getVsp() const { return m_vsp; } float MainObject::getFrame() const { return m_frame; } float MainObject::getFrameStart() const { return m_frameStart; } float MainObject::getFrameEnd() const { return m_frameEnd; } float MainObject::getImageXscale() const { return m_imageXscale; } float MainObject::getImageYscale() const { return m_imageYscale; } float MainObject::getImageScale() const { return m_imageScale; } float MainObject::getImageAngle() const { return m_imageAngle; } float MainObject::getXOffset() const { return m_xOffset; } float MainObject::getYOffset() const { return m_yOffset; } float MainObject::getX() const { return m_x; } float MainObject::getY() const { return m_y; } float MainObject::getXStart() const { return m_xStart; } float MainObject::getYStart() const { return m_yStart; } float MainObject::getXPrevious() const { return m_xPrevious; } float MainObject::getYPrevious() const { return m_yPrevious; } float MainObject::getTime() const { return m_time; } int MainObject::getInstanceId() const { return m_instanceId; } unsigned int MainObject::getMaskW() const { return m_w; } unsigned int MainObject::getMaskH() const { return m_h; } bool MainObject::getIsActive() const { return m_isActive; } int MainObject::getImageAlpha() const { return m_imageAlpha; } int MainObject::getImageIndex() const { return m_imageIndex; } int MainObject::getSpriteWidth() const { return m_sprParent.getTextureRect().width; } int MainObject::getSpriteHeight() const { return m_sprParent.getTextureRect().height; } float MainObject::getSpriteX() const { return m_sprParent.getPosition().x; } float MainObject::getSpriteY() const { return m_sprParent.getPosition().y; } int MainObject::getTextureWidth() const { return is::getSFMLTextureWidth(m_sprParent.getTexture()); } int MainObject::getTextureHeight() const { return is::getSFMLTextureHeight(m_sprParent.getTexture()); } int MainObject::getSpriteCenterX() const { return (m_sprParent.getTextureRect().width / 2); } int MainObject::getSpriteCenterY() const { return (m_sprParent.getTextureRect().height / 2); } int MainObject::getSpriteNumberSubImage(int subImageWidth) const { return (m_sprParent.getTexture()->getSize().x / subImageWidth); } bool MainObject::placeMettingSubFunction(float x, float y, MainObject const *other) const { is::Rectangle testRec = this->getMask(); testRec.m_left += x; testRec.m_right += x; testRec.m_top += y; testRec.m_bottom += y; is::Circle testCircle = this->getCircleMask(); testCircle.m_x += x; testCircle.m_y += y; is::Rectangle otherRectangle = other->getMask(); is::Circle otherCircle = other->getCircleMask(); if (m_w > 0 && m_h > 0 && other->getMaskW() > 0 && other->getMaskH() > 0) { return (is::collisionTest(testRec, otherRectangle)); } else if ((m_w == 0 || m_h == 0) && other->getMaskW() > 0 && other->getMaskH() > 0) { return (is::collisionTest(testCircle, otherRectangle)); } else if ((m_w > 0 && m_h > 0) && (other->getMaskW() == 0 || other->getMaskH() == 0)) { return (is::collisionTest(testRec, otherCircle)); } else if ((m_w == 0 || m_h == 0) && testCircle.m_raduis > 0.f && (other->getMaskW() == 0 || other->getMaskH() == 0) && otherCircle.m_raduis > 0.f) { return (is::collisionTest(testCircle, otherCircle)); } else { is::showLog("ERROR: Badly defined collision masks between these objects: <" + m_strName + "> & <" + other->getName() + ">", true); } return false; } void MainObject::updateSDMsprite() { if (m_isSDMSprite) { if (m_centerSpr) centerCollisionMask(m_x, m_y); else updateCollisionMask(); updateSprite(); } } bool MainObject::placeMetting(int x, int y, MainObject const *other) { return placeMettingSubFunction(x, y, other); } bool MainObject::placeMetting(int x, int y, std::shared_ptr const &other) { return placeMettingSubFunction(x, y, other.get()); } bool MainObject::inViewRec(sf::View const &view, bool useTexRec) { is::Rectangle testRec; if (useTexRec) { testRec.m_left = m_x; testRec.m_top = m_y; testRec.m_right = m_x + ((m_sprParent.getGlobalBounds().width < 1) ? 32 : m_sprParent.getGlobalBounds().width); testRec.m_bottom = m_y + ((m_sprParent.getGlobalBounds().height < 1) ? 32 : m_sprParent.getGlobalBounds().height); } else testRec = this->getMask(); is::Rectangle viewRec; viewRec.m_left = view.getCenter().x - (view.getSize().x / 2.f); viewRec.m_right = view.getCenter().x + (view.getSize().x / 2.f); viewRec.m_top = view.getCenter().y - (view.getSize().y / 2.f); viewRec.m_bottom = view.getCenter().y + (view.getSize().y / 2.f); if (is::collisionTest(testRec, viewRec)) return true; return false; } sf::Sprite& MainObject::getSprite() { return m_sprParent; } float COMPARE_DISTANCE(460.f); bool operator<(const MainObject *a, const MainObject &b) { if (is::instanceExist(a)) return a->getX() < (b.getX() - COMPARE_DISTANCE); else return false; } bool operator<(const MainObject &b, const MainObject *a) { if (is::instanceExist(a)) return (b.getX() + COMPARE_DISTANCE) < a->getX(); else return false; } bool operator<(std::shared_ptr const &a, const MainObject &b) { if (is::instanceExist(a)) return a->getX() < (b.getX() - COMPARE_DISTANCE); else return false; } bool operator<(const MainObject &b, std::shared_ptr const &a) { if (is::instanceExist(a)) return (b.getX() + COMPARE_DISTANCE) < a->getX(); else return false; } } ================================================ FILE: app/src/main/cpp/PCH.hpp ================================================ #ifndef PRECOMPILED_HEADER_HPP #define PRECOMPILED_HEADER_HPP // Allow to use icon for Windows OS applications #define IS_ENGINE_VS_CODE #ifndef _DEBUG #ifndef NDEBUG #define NDEBUG #endif #endif // _DEBUG // Raspberry Pi #ifdef SFML_SYSTEM_LINUX #ifdef __arm__ #define SFML_SYSTEM_PI #endif #endif // SFML SYSTEM_LINUX // Typical stdafx.h #include #include #include #include #include #include #include #include #include #include #include // Additional C/C++ libs #include #include #include #include #include #include #include #include #include #include #include #include // Windows #ifdef _WIN32 #ifndef UNICODE #define UNICODE #endif #ifndef _UNICODE #define _UNICODE #endif #define WIN32_LEAN_AND_MEAN #include #endif // _WIN32 // Utils #include "vscode/Utility/Types.hpp" // Macros #define _UNUSED(x) (void)(x) #endif // PRECOMPILED_HEADER_HPP ================================================ FILE: app/src/main/cpp/SDL_android_main.c ================================================ #if !defined(IS_ENGINE_VS_CODE) && !defined(IS_ENGINE_SWITCH) #ifdef __ANDROID__ /* SDL_android_main.c, placed in the public domain by Sam Lantinga 3/13/14 */ #include "SDL_internal.h" /* Include the SDL main definition header */ #include "SDL_main.h" /******************************************************************************* Functions called by JNI *******************************************************************************/ #include /* Called before SDL_main() to initialize JNI bindings in SDL library */ extern void SDL_Android_Init(JNIEnv* env, jclass cls); /* This prototype is needed to prevent a warning about the missing prototype for global function below */ JNIEXPORT int JNICALL Java_com_author_isengine_SDLActivity_nativeInit(JNIEnv* env, jclass cls, jobject array); /* Start up the SDL app */ JNIEXPORT int JNICALL Java_com_author_isengine_SDLActivity_nativeInit(JNIEnv* env, jclass cls, jobject array) { int i; int argc; int status; int len; char** argv; /* This interface could expand with ABI negotiation, callbacks, etc. */ SDL_Android_Init(env, cls); SDL_SetMainReady(); /* Prepare the arguments. */ len = (*env)->GetArrayLength(env, array); argv = SDL_stack_alloc(char*, 1 + len + 1); argc = 0; /* Use the name "app_process" so PHYSFS_platformCalcBaseDir() works. https://bitbucket.org/MartinFelis/love-android-sdl2/issue/23/release-build-crash-on-start */ argv[argc++] = SDL_strdup("app_process"); for (i = 0; i < len; ++i) { const char* utf; char* arg = NULL; jstring string = (*env)->GetObjectArrayElement(env, array, i); if (string) { utf = (*env)->GetStringUTFChars(env, string, 0); if (utf) { arg = SDL_strdup(utf); (*env)->ReleaseStringUTFChars(env, string, utf); } (*env)->DeleteLocalRef(env, string); } if (!arg) { arg = SDL_strdup(""); } argv[argc++] = arg; } argv[argc] = NULL; /* Run the application. */ status = SDL_main(argc, argv); /* Release the arguments. */ for (i = 0; i < argc; ++i) { SDL_free(argv[i]); } SDL_stack_free(argv); /* Do not issue an exit or the whole application will terminate instead of just the SDL thread */ /* exit(status); */ return status; } #endif /* __ANDROID__ */ /* vi: set ts=4 sw=4 expandtab: */ #endif /* IS_ENGINE_VS_CODE */ ================================================ FILE: app/src/main/cpp/TransitionEffect.cpp ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2024 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/system/graphic/TransitionEffect.h" namespace is { TransitionEffect::TransitionEffect(is::GameDisplay *scene): MainObject(), Type(FADE_OUT), m_scene(scene), m_transitionEnd(false) { m_strName = "TransitionEffect"; m_imageAlpha = 255; #if defined(IS_ENGINE_USE_SDM) m_depth = -999999999; #endif // transition rectangle is::createRectangle(m_recTransition, sf::Vector2f(scene->getViewW() + 10.f, scene->getViewH() + 10.f), sf::Color(0, 0, 0, m_imageAlpha), scene->getViewW() / 2.f, scene->getViewH() / 2.f); } void TransitionEffect::step(const float &DELTA_TIME) { m_x = m_scene->getViewX(); m_y = m_scene->getViewY(); // transition fade out if (m_type == FADE_OUT) { if (m_imageAlpha > 5) { m_imageAlpha -= static_cast((5.f * is::VALUE_CONVERSION) * DELTA_TIME); m_transitionEnd = false; } else { m_imageAlpha = 0; m_transitionEnd = true; } } // transition fade in if (m_type == FADE_IN) { if (m_imageAlpha < 250) { m_imageAlpha += static_cast((5.f * is::VALUE_CONVERSION) * DELTA_TIME); m_transitionEnd = false; } else { m_imageAlpha = 255; m_transitionEnd = true; } } is::setSFMLObjFillColor(m_recTransition, sf::Color(0, 0, 0, m_imageAlpha)); is::setSFMLObjX_Y(m_recTransition, m_x, m_y); } void TransitionEffect::draw(is::Render &render) { is::draw(render, m_recTransition); } } ================================================ FILE: app/src/main/cpp/app_src/activity/GameActivity.h ================================================ #ifndef GAMEACTIVITY_H_INCLUDED #define GAMEACTIVITY_H_INCLUDED #include "../scenes/HelloScene/HelloScene.h" // example // #include "../scenes/YourScene/YourScene.h" //////////////////////////////////////////////////////////// /// Allows to manage the different scenes of the game //////////////////////////////////////////////////////////// class GameActivity { private: std::shared_ptr m_gameScene; public: bool m_changeActivity = false; GameActivity(is::GameSystemExtended &gameSysExt) { m_gameScene = nullptr; //////////////////////////////////////////////////////////// // Allows to choose the scene that will be launched switch (gameSysExt.m_launchOption) { case is::DisplayOption::HELLO_SCENE: m_gameScene = std::make_shared(gameSysExt); break; /* * /!\ WARNING! /!\ * This constructor is no longer supported in this version of the engine. Use the one in the example. * * m_gameScene = std::make_shared(activityCtrl.getWindow(), getView(), m_surface, gameSysExt); */ // example // case is::DisplayOption::YOUR_SCENE: // m_gameScene = std::make_shared(gameSysExt); // break; default: is::showLog("ERROR: Scene not found !", true); break; } m_gameScene->loadResources(); } virtual void onUpdate() { if (m_gameScene->getIsRunning()) m_gameScene->step(); else { if (!m_changeActivity) m_changeActivity = true; } } virtual void onDraw() {m_gameScene->drawScreen();} virtual ~GameActivity() {} }; #endif // GAMEACTIVITY_H_INCLUDED ================================================ FILE: app/src/main/cpp/app_src/config/ExtraConfig.h ================================================ #ifndef EXTRACONFIG_H_INCLUDED #define EXTRACONFIG_H_INCLUDED // Uncomment to enable this function // #define IS_ENGINE_USE_RENDER_TEXTURE ///< This feature coming soon ! /* * 1) Uncomment this line (definition of the Preporcessor which is below) to use the engine's main render loop. * 2) But if you want to use a basic SFML render loop comment out this line. * It activates the "basicSFMLmain()" function which launches an SFML window. * Very useful if you already have an existing project and want to integrate it into the engine. * The "basicSFMLmain()" function is implemented in the "basicSFMLmain.cpp" file (found in the "cpp" directory). * It is in this file that you can associate your code with that of the engine. */ #define IS_ENGINE_USE_MAIN_LOOP ///< Allows to use the engine's main render loop // Uncomment to enable SDM function #define IS_ENGINE_USE_SDM ///< Allows to use Step and Draw Manager #define SND_FILE_EXTENSION ".wav" // Uncomment to enable this function #define IS_ENGINE_OPTIMIZE_PERF ///< Allows to activate the optimization in certain parts of the engine // Uncomment to enable SHOW LOG function #define IS_ENGINE_USE_SHOWLOG ///< Allows to show log in console #include #include #include #include #include "../../isEngine/system/islibconnect/isLibConnect.h" namespace is { inline bool fileExist(std::string const &fileName) { std::ifstream file(fileName.c_str()); return !file.fail(); } } #endif // EXTRACONFIG_H_INCLUDED ================================================ FILE: app/src/main/cpp/app_src/config/GameConfig.h ================================================ #ifndef GAMECONFIG_H_INCLUDED #define GAMECONFIG_H_INCLUDED #include "ExtraConfig.h" namespace is { //////////////////////////////////////////////////////////// /// \brief Allows to manipulate the different scenes and options /// //////////////////////////////////////////////////////////// enum DisplayOption { HELLO_SCENE, ///< Access the hello scene // example // YOUR_SCENE, ///< Access to your scene }; //////////////////////////////////////////////////////////// /// \brief Allows to manipulate the window style /// //////////////////////////////////////////////////////////// enum WindowStyle { NONE, ///< No decoration at all TITLEBAR, ///< The window has a titlebar RESIZE, ///< The window can be resized and has a maximize button CLOSE, ///< The window has a close button FULLSCREEN, ///< The window is shown in fullscreen mode DEFAULT ///< The default style, which is a shortcut for Titlebar | Resize | Close }; //////////////////////////////////////////////////////////// /// Allows to define the general parameters of the game and /// that the ad manager (Admob) //////////////////////////////////////////////////////////// namespace GameConfig { static const unsigned int WINDOW_WIDTH = 640; ///< Window width work only for PC Platform static const unsigned int WINDOW_HEIGHT = 480; ///< Window height work only for PC Platform static const float VIEW_WIDTH = 640.f; static const float VIEW_HEIGHT = 480.f; static const float FPS = 60.f; ///< Game FPS static const is::WindowStyle WINDOW_SETTINGS = WindowStyle::DEFAULT; ///< Window style static const DisplayOption LAUNCH_OPTION = DisplayOption::HELLO_SCENE; ///< Represents the first scene to be launched /// Represent the key which validates the options with the Mouse static sf::Mouse::Button KEY_VALIDATION_MOUSE = sf::Mouse::Left; /// Represent the key which validates the options with the Keyboard static sf::Keyboard::Key KEY_VALIDATION_KEYBOARD = sf::Keyboard::Return; /// Represent the key which cancel the options with the Keyboard static sf::Keyboard::Key KEY_CANCEL = sf::Keyboard::Escape; /// Represents the button A key static sf::Keyboard::Key KEY_A = sf::Keyboard::W; /// Represents the button B key static sf::Keyboard::Key KEY_B = sf::Keyboard::X; /// Represents the button X key static sf::Keyboard::Key KEY_X = sf::Keyboard::Q; /// Represents the button Y key static sf::Keyboard::Key KEY_Y = sf::Keyboard::S; /// Represents the Left directional key static sf::Keyboard::Key KEY_LEFT = sf::Keyboard::Left; /// Represents the Right directional key static sf::Keyboard::Key KEY_RIGHT = sf::Keyboard::Right; /// Represents the Up directional key static sf::Keyboard::Key KEY_UP = sf::Keyboard::Up; /// Represents the Down directional key static sf::Keyboard::Key KEY_DOWN = sf::Keyboard::Down; //////////////////////////////////////////////////////////// /// Default values that SFML texts will take when they are created //////////////////////////////////////////////////////////// static const int DEFAULT_SFML_TEXT_SIZE = 20; static const sf::Color &DEFAULT_SFML_TEXT_COLOR = sf::Color::Blue; static const sf::Color &DEFAULT_SFML_SELECTED_TEXT_COLOR = sf::Color::White; static const int DEFAULT_MSG_BOX_TEXT_SIZE = 20; static const int DEFAULT_MSG_BOX_BUTTON_TEXT_SIZE = 18; static const sf::Color &DEFAULT_MSG_BOX_TEXT_COLOR = sf::Color::White; static const sf::Color &DEFAULT_MSG_BOX_SELECTED_TEXT_COLOR = sf::Color::Red; static const int DEFAULT_RPG_DIALOG_TEXT_SIZE = 16; static const int DEFAULT_RPG_DIALOG_BUTTON_TEXT_SIZE = 13; static const sf::Color &DEFAULT_RPG_DIALOG_TEXT_COLOR = sf::Color::White; static const sf::Color &DEFAULT_RPG_DIALOG_SELECTED_TEXT_COLOR = sf::Color::Blue; static const sf::Color &DEFAULT_BUTTON_TEXT_COLOR = sf::Color::White; static const std::string MAJOR = "1"; ///< Game major version static const std::string MINOR = "0"; ///< Game minor version inline std::string getGameVersion() {return MAJOR + "." + MINOR;} ///< return version of the game static std::string const GAME_NAME = "Hello"; ///< Windows title name static std::string const GAME_AUTHOR = "Author"; // parent directory static std::string const ASSETS_DIR = #if !defined(__ANDROID__) #if defined(IS_ENGINE_QT) (fileExist("assets/assets.dat") ? "" : "../../") + #endif std::string("assets/"); #elif defined(IS_ENGINE_HTML_5) "/assets/" #elif defined(IS_ENGINE_SWITCH) "assets/" #else ""; #endif // defined static std::string const GUI_DIR = ASSETS_DIR + "image/gui/"; ///< Path to resource files that serve as GUI static std::string const FONT_DIR = ASSETS_DIR + "font/"; ///< Path to resource files that serve as Font static std::string const SPRITES_DIR = ASSETS_DIR + "image/sprites/"; ///< Path to resource files that serve as Sprite static std::string const TILES_DIR = ASSETS_DIR + "image/tiles/"; ///< Path to resource files that serve as tile and background static std::string const SFX_DIR = ASSETS_DIR + "sound/sfx/"; ///< Path to resource files that serve as SFX static std::string const MUSIC_DIR = ASSETS_DIR + "sound/music/"; ///< Path to resource files that serve as Music static std::string const TMX_RSC_DIR = ASSETS_DIR + "maps/"; ///< Path to TMX resource files #if defined(__ANDROID__) //////////////////////////////////////////////////////////// /// \brief game package name /// Represents the place where your data will be saved on Android /// Replace this with your real package name (e.g. com.StudioName.GameName) /// You must apply this name for the applicationId in the build.gradle file //////////////////////////////////////////////////////////// static std::string const PACKAGE_NAME = "com.author.isengine"; #endif // defined // parent directory static std::string DATA_PARENT_DIR = #if defined(__ANDROID__) "/data/data/" + PACKAGE_NAME + "/" #elif defined(IS_ENGINE_HTML_5) "/save/" #else "save/" #endif ; ///< Path to save game progress file static std::string const GAME_DATA_FILE = DATA_PARENT_DIR + "game_data.bin"; ///< Path to save game menu configuration file static std::string const CONFIG_FILE = DATA_PARENT_DIR + "game_config.dat"; ///< Path to save game pad configuration file static std::string const GAME_PAD_FILE = DATA_PARENT_DIR + "game_pad_config.dat"; } } #endif // GAMECONFIG_H_INCLUDED ================================================ FILE: app/src/main/cpp/app_src/gamesystem_ext/GameSystemExtended.h ================================================ #ifndef GAMESYSTEMEXTENDED_H_INCLUDED #define GAMESYSTEMEXTENDED_H_INCLUDED #include "../../isEngine/system/function/GameSystem.h" #include "../levels/Level.h" //////////////////////////////////////////////////////////// /// \brief Class derived from GameSystem to manage the /// different engine components /// /// Allows to manage the saving / loading of game and /// configuration data, manipulate the data of the virtual /// Game Pad and act on the different scenes of the game //////////////////////////////////////////////////////////// namespace is { class GameSystemExtended : public GameSystem { public: GameSystemExtended(sf::RenderWindow &window); /// Initialize the data link to the game engine void initSystemData(); /// Initialize game progress data void initProgress(); /// Initialize game play data (bonus, score, ...) void initData(bool clearCurrentLevel = true); /// Save game play data void saveData(std::string const &fileName); /// Load game play data void loadData(std::string const &fileName); /// Allows to choose the scene that will be launched is::DisplayOption m_launchOption = is::GameConfig::LAUNCH_OPTION; // Represents the first scene to be launched int m_gameProgression; int m_levelNumber; int m_currentLevel; int m_currentLives; int m_currentBonus; int m_currentScore; int m_currentHiScore; int m_levelTime; }; } #endif // GAMESYSTEMEXTENDED_H_INCLUDED ================================================ FILE: app/src/main/cpp/app_src/language/GameLanguage.h ================================================ #ifndef GAMELANGUAGE_H_INCLUDED #define GAMELANGUAGE_H_INCLUDED #include "../../isEngine/system/function/GameKeyName.h" /* * This file allows you to define the languages you want to use in the game. * To use a language you must define a array. the first index represents the first language * (e.g lang_array[0] => eng) and the index following its translation (e.g lang_array[1] => fr). * * example to display several sentences in the dialog manager. * lang_array[] = {"eng 1", "translation fr 1", "eng 2", "translation fr 2", ...}; * (Go to the GameDialog Class to see how we implement languages to make sentences in dialogs) */ namespace is { ////////////////////////////////////////////////////// /// \brief Access to content that allows internationalization of the game /// ////////////////////////////////////////////////////// namespace lang { //////////////////////////////////////////////////////////// /// \brief Represent the index of each language /// //////////////////////////////////////////////////////////// enum GameLanguage { ENGLISH, ///< English language index FRANCAIS, ///< French language index // example // YOUR_LANGUAGE ///< Your language index }; // ------------------------ message box answer ------------------------ static std::string pad_answer_ok[] = {"OK" , "OK"}; static std::string pad_answer_yes[] = {"YES", "OUI"}; static std::string pad_answer_no[] = {"NO" , "NON"}; // ------------------------ message box sentences ------------------------ static std::string msg_quit_game[] = {"Quit Game", "Quitter le Jeu"}; // example // static std::string msg_your_message[] = {"Message in English", "Message in French"}; // ------------------------ RPG dialog ------------------------ static std::string pad_dialog_skip[] = {"Skip", "Passer"}; static std::string dialog_hello[] = {"Hello !", "Salut !", "Ready to make the future best game in the world!", "En route pour invinter le futur meilleur jeu du monde !"}; // example // static std::wstring dialog_your_dialog[] = {L"Represents the first message of the dialog in English", // L"Represents the first message of the dialog in French", // L"Represents the second message of the dialog in English", // L"Represents the second message of the dialog in French"}; } } #endif // GAMELANGUAGE_H_INCLUDED ================================================ FILE: app/src/main/cpp/app_src/levels/Level.h ================================================ #ifndef LEVEL_H_INCLUDED #define LEVEL_H_INCLUDED /* * This file allows to include the level header generated by the is::Engine * level editor to be able to use them with the engine components */ // #include "../levels/level_1.h" // ... namespace is { ////////////////////////////////////////////////////// /// \brief Access the content which allows to manage /// the integration of game levels /// ////////////////////////////////////////////////////// namespace level { //////////////////////////////////////////////////////////// /// \brief Enum represents a array containing the level data /// /// Allows to identify and load the corresponding level /// array in GameLevel //////////////////////////////////////////////////////////// enum LevelId { LEVEL_1 // ... //////////////////////////////////////////////////////////// // don't touch this. Add all level item before this one , LEVEL_MAX ///< Allows you to know the number of levels //////////////////////////////////////////////////////////// }; //////////////////////////////////////////////////////////// /// \brief Returns the level array according to its index /// /// It is used in @a GameLevelLoadResource() to determine the /// level array that will be loaded to create the game level //////////////////////////////////////////////////////////// inline short const* getLevelMap(int CURRENT_LEVEL) { switch (CURRENT_LEVEL) { // case LEVEL_1 : return LEVEL_1_MAP; break; // ... default: return 0; break; } } } } #endif // LEVEL_H_INCLUDED ================================================ FILE: app/src/main/cpp/app_src/objects/HelloWorld.h ================================================ #pragma once #include "../../isEngine/system/display/GameDisplay.h" ////////////////////////////////////////////////////// /// \brief This class allows to scroll a "Hello world!" /// on the screen and perform touch / click detection tests ////////////////////////////////////////////////////// class HelloWorld : public is::MainObject { public: HelloWorld(float x, float y, is::GameDisplay *scene): is::MainObject(x ,y), m_scene(scene) { // Create the sprite of object and center it m_xOffset = 80.f; // set offset x to center the image m_yOffset = 82.f; // set offset y to center the image is::createSprite(scene->GRMgetTexture("hello_world"), m_sprParent, sf::Vector2f(m_x, m_y), sf::Vector2f(m_xOffset, m_yOffset)); // Display depth of the object in the scene m_depth = -1; } void step(float const &DELTA_TIME) { // When you touch (on Android) or click on the sprite of the object it moves down (in pixel) if (m_scene->mouseCollision(m_sprParent) && m_scene->getGameSystem().isPressed()) { // VALUE_CONVERSION : allows to avoid using large values to do operations // (basically it converts small values to large) and it also acts on the timing // of the program m_imageScale += (is::VALUE_CONVERSION * 0.05f) * DELTA_TIME; // scale the image if (m_imageScale > 2.f) m_imageScale = 1.f; m_imageXscale = m_imageScale; m_imageYscale = m_imageScale; m_y += (is::VALUE_CONVERSION * 1.f) * DELTA_TIME; // move (down) object on y axis } // moved the object to the right (in pixel) m_x += (is::VALUE_CONVERSION * 2.f) * DELTA_TIME; // If the object comes out to the right or at the top of the scene, bring it back // to its starting position // By default the size of the scene is equal to that of the window if (m_x > m_scene->getSceneWidth()) m_x = m_xStart - (m_xOffset * 2.f); if (m_y > m_scene->getSceneHeight() - 32.f) m_y = m_yStart - m_yOffset; updateSprite(); // update the sprite with all object variables (x, y, angle, scale, ...) } private: is::GameDisplay *m_scene; }; ================================================ FILE: app/src/main/cpp/app_src/objects/widgets/GameDialog.h ================================================ #ifndef GAMEDIALOG_H_INCLUDED #define GAMEDIALOG_H_INCLUDED #include #include "../../../isEngine/system/display/GameDisplay.h" #include "../../language/GameLanguage.h" //////////////////////////////////////////////////////////// /// \brief Displays and manages game dialogs /// //////////////////////////////////////////////////////////// namespace is { class GameDialog : public is::MainObject { public: GameDialog(sf::Texture &tex, sf::Font &fnt, is::GameDisplay *scene); //////////////////////////////////////////////////////////// /// \brief Represents the different dialogs /// /// Each enum is linked to an array of string representing /// a dialogs found in GameLanguage.h //////////////////////////////////////////////////////////// enum DialogIndex { DIALOG_NONE = -1, DIALOG_HELLO, // example // DIALOG_YOUR_DIALOG }; //////////////////////////////////////////////////////////// /// \brief Link enum to an array of string representing the /// dialog in GameLanguage.h /// /// Each time an array of string representing a dialog is added /// it must be linked to its enum to be able to launch it later /// with the setDialog() method. //////////////////////////////////////////////////////////// void linkArrayToEnum() { auto setMsg = [this](std::string txt) { m_strDialog = txt; }; auto checkMsg =[this, &setMsg](std::string txt[]) { if (m_msgIndex < m_msgIndexMax) setMsg(txt[m_msgIndex + m_scene->getGameSystem().m_gameLanguage]); }; // each enum with its array of string switch (m_dialogIndex) { case DIALOG_HELLO: m_msgIndexMax = is::arraySize(is::lang::dialog_hello); checkMsg(is::lang::dialog_hello); break; // example // case DIALOG_YOUR_DIALOG: // m_msgIndexMax = is::arraySize(is::lang::dialog_your_dialog); // checkMsg(is::lang::dialog_your_dialog); // break; default: break; } } void step(const float &DELTA_TIME); /// launch a dialog based on the enumeration index void setDialog(DialogIndex dialogIndex); void setMouseInCollison(bool val); void draw(is::Render &surface); DialogIndex getDialogIndex() const { return m_dialogIndex; } bool getMouseInCollison() const { return m_mouseInCollison; } bool showDialog() const { return m_showDialog; } private: is::GameDisplay *m_scene; sf::Text m_txtDialog, m_txtSkip; std::string m_strDialog; sf::Sprite m_sprNext, m_sprSkip; bool m_showDialog, m_mouseInCollison, m_dialogEnd, m_newLine; int m_msgIndex, m_msgIndexMax, m_size; float m_blindTime; DialogIndex m_dialogIndex; }; } #endif // GAMEDIALOG_H_INCLUDED ================================================ FILE: app/src/main/cpp/app_src/scenes/HelloScene/HelloScene.h ================================================ #pragma once #include "../../../isEngine/system/entity/Background.h" #include "../../objects/widgets/GameDialog.h" #include "../../objects/HelloWorld.h" class HelloScene : public is::GameDisplay { public: /* /!\ WARNING! /!\ * This constructor is no longer supported in this version of the engine. Use the one below. * * HelloScene(sf::RenderWindow &window, sf::View &view, is::Render &surface, is::GameSystemExtended &gameSysExt): * GameDisplay(window, view, surface, gameSysExt, sf::Color::White) {} */ HelloScene(is::GameSystemExtended &gameSysExt): GameDisplay(gameSysExt, sf::Color::White /* => scene color*/) {} void loadResources() { m_gameSysExt.m_gameLanguage = is::lang::ENGLISH; // set default game language // uncomment to change English language in French // m_gameSysExt.m_gameLanguage = is::lang::FRANCAIS; // load font and texture GameDisplay::loadParentResources(); // allows to load system resource (very important never forgot to call him) GRMaddTexture("is_engine_logo", is::GameConfig::SPRITES_DIR + "is_engine_logo.png"); GRMaddTexture("hello_world", is::GameConfig::SPRITES_DIR + "hello_world.png"); auto &texBg = GRMaddTexture("background", is::GameConfig::TILES_DIR + "background.png"); auto &texDialog = GRMaddTexture("dialog_box", is::GameConfig::GUI_DIR + "dialog_box.png"); // add a background to the position x = 0, y = 0 which will fill the scene and which will be scrolled (scrolling speed = 0.5) SDMaddSceneObject(std::make_shared(texBg, 0.f, 0.f, this, 0.5f, -0.5f, false, false)); // add an SFML sprite which will be above the background and which will have the name "Logo" (by default x = 0 and y = 0) SDMaddSprite(GRMgetTexture("is_engine_logo"), "Logo", 0.f, 62.f, false, -1); // add an object at position x = 0, y = 0 which will be updated and displayed in the scene SDMaddSceneObject(std::make_shared(0.f, 0.f, this)); // add RPG style game dialog auto gameDialog = std::make_shared(texDialog, GRMgetFont("font_msg"), this); gameDialog->setDepth(-2); // the display depth (make it appear on all objects. The object with the smallest value appears on the others) gameDialog->setDialog(is::GameDialog::DialogIndex::DIALOG_HELLO); // set the corresponding dialog (See GameDialog.h and GameLanguage.h for more details on creating a message for dialogue) SDMaddSceneObject(gameDialog); // add and play music GSMaddSound("game_music", is::GameConfig::MUSIC_DIR + "game_music.wav"); GSMplaySound("game_music"); GSMsetSoundLoop("game_music", true); } }; ================================================ FILE: app/src/main/cpp/b2BlockAllocator.cpp ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Common/b2BlockAllocator.h" #include #include #include #include int32 b2BlockAllocator::s_blockSizes[b2_blockSizes] = { 16, // 0 32, // 1 64, // 2 96, // 3 128, // 4 160, // 5 192, // 6 224, // 7 256, // 8 320, // 9 384, // 10 448, // 11 512, // 12 640, // 13 }; uint8 b2BlockAllocator::s_blockSizeLookup[b2_maxBlockSize + 1]; bool b2BlockAllocator::s_blockSizeLookupInitialized; struct b2Chunk { int32 blockSize; b2Block* blocks; }; struct b2Block { b2Block* next; }; b2BlockAllocator::b2BlockAllocator() { b2Assert(b2_blockSizes < UCHAR_MAX); m_chunkSpace = b2_chunkArrayIncrement; m_chunkCount = 0; m_chunks = (b2Chunk*)b2Alloc(m_chunkSpace * sizeof(b2Chunk)); memset(m_chunks, 0, m_chunkSpace * sizeof(b2Chunk)); memset(m_freeLists, 0, sizeof(m_freeLists)); if (s_blockSizeLookupInitialized == false) { int32 j = 0; for (int32 i = 1; i <= b2_maxBlockSize; ++i) { b2Assert(j < b2_blockSizes); if (i <= s_blockSizes[j]) { s_blockSizeLookup[i] = (uint8)j; } else { ++j; s_blockSizeLookup[i] = (uint8)j; } } s_blockSizeLookupInitialized = true; } } b2BlockAllocator::~b2BlockAllocator() { for (int32 i = 0; i < m_chunkCount; ++i) { b2Free(m_chunks[i].blocks); } b2Free(m_chunks); } void* b2BlockAllocator::Allocate(int32 size) { if (size == 0) return NULL; b2Assert(0 < size); if (size > b2_maxBlockSize) { return b2Alloc(size); } int32 index = s_blockSizeLookup[size]; b2Assert(0 <= index && index < b2_blockSizes); if (m_freeLists[index]) { b2Block* block = m_freeLists[index]; m_freeLists[index] = block->next; return block; } else { if (m_chunkCount == m_chunkSpace) { b2Chunk* oldChunks = m_chunks; m_chunkSpace += b2_chunkArrayIncrement; m_chunks = (b2Chunk*)b2Alloc(m_chunkSpace * sizeof(b2Chunk)); memcpy(m_chunks, oldChunks, m_chunkCount * sizeof(b2Chunk)); memset(m_chunks + m_chunkCount, 0, b2_chunkArrayIncrement * sizeof(b2Chunk)); b2Free(oldChunks); } b2Chunk* chunk = m_chunks + m_chunkCount; chunk->blocks = (b2Block*)b2Alloc(b2_chunkSize); #if defined(_DEBUG) memset(chunk->blocks, 0xcd, b2_chunkSize); #endif int32 blockSize = s_blockSizes[index]; chunk->blockSize = blockSize; int32 blockCount = b2_chunkSize / blockSize; b2Assert(blockCount * blockSize <= b2_chunkSize); for (int32 i = 0; i < blockCount - 1; ++i) { b2Block* block = (b2Block*)((int8*)chunk->blocks + blockSize * i); b2Block* next = (b2Block*)((int8*)chunk->blocks + blockSize * (i + 1)); block->next = next; } b2Block* last = (b2Block*)((int8*)chunk->blocks + blockSize * (blockCount - 1)); last->next = NULL; m_freeLists[index] = chunk->blocks->next; ++m_chunkCount; return chunk->blocks; } } void b2BlockAllocator::Free(void* p, int32 size) { if (size == 0) { return; } b2Assert(0 < size); if (size > b2_maxBlockSize) { b2Free(p); return; } int32 index = s_blockSizeLookup[size]; b2Assert(0 <= index && index < b2_blockSizes); #ifdef _DEBUG // Verify the memory address and size is valid. int32 blockSize = s_blockSizes[index]; bool found = false; for (int32 i = 0; i < m_chunkCount; ++i) { b2Chunk* chunk = m_chunks + i; if (chunk->blockSize != blockSize) { b2Assert( (int8*)p + blockSize <= (int8*)chunk->blocks || (int8*)chunk->blocks + b2_chunkSize <= (int8*)p); } else { if ((int8*)chunk->blocks <= (int8*)p && (int8*)p + blockSize <= (int8*)chunk->blocks + b2_chunkSize) { found = true; } } } b2Assert(found); memset(p, 0xfd, blockSize); #endif b2Block* block = (b2Block*)p; block->next = m_freeLists[index]; m_freeLists[index] = block; } void b2BlockAllocator::Clear() { for (int32 i = 0; i < m_chunkCount; ++i) { b2Free(m_chunks[i].blocks); } m_chunkCount = 0; memset(m_chunks, 0, m_chunkSpace * sizeof(b2Chunk)); memset(m_freeLists, 0, sizeof(m_freeLists)); } ================================================ FILE: app/src/main/cpp/b2Body.cpp ================================================ /* * Copyright (c) 2006-2007 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Dynamics/b2Body.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Fixture.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2World.h" #include "isEngine/ext_lib/Box2D/Dynamics/Contacts/b2Contact.h" #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2Joint.h" b2Body::b2Body(const b2BodyDef* bd, b2World* world) { b2Assert(bd->position.IsValid()); b2Assert(bd->linearVelocity.IsValid()); b2Assert(b2IsValid(bd->angle)); b2Assert(b2IsValid(bd->angularVelocity)); b2Assert(b2IsValid(bd->angularDamping) && bd->angularDamping >= 0.0f); b2Assert(b2IsValid(bd->linearDamping) && bd->linearDamping >= 0.0f); m_flags = 0; if (bd->bullet) { m_flags |= e_bulletFlag; } if (bd->fixedRotation) { m_flags |= e_fixedRotationFlag; } if (bd->allowSleep) { m_flags |= e_autoSleepFlag; } if (bd->awake) { m_flags |= e_awakeFlag; } if (bd->active) { m_flags |= e_activeFlag; } m_world = world; m_xf.p = bd->position; m_xf.q.Set(bd->angle); m_sweep.localCenter.SetZero(); m_sweep.c0 = m_xf.p; m_sweep.c = m_xf.p; m_sweep.a0 = bd->angle; m_sweep.a = bd->angle; m_sweep.alpha0 = 0.0f; m_jointList = NULL; m_contactList = NULL; m_prev = NULL; m_next = NULL; m_linearVelocity = bd->linearVelocity; m_angularVelocity = bd->angularVelocity; m_linearDamping = bd->linearDamping; m_angularDamping = bd->angularDamping; m_gravityScale = bd->gravityScale; m_force.SetZero(); m_torque = 0.0f; m_sleepTime = 0.0f; m_type = bd->type; if (m_type == b2_dynamicBody) { m_mass = 1.0f; m_invMass = 1.0f; } else { m_mass = 0.0f; m_invMass = 0.0f; } m_I = 0.0f; m_invI = 0.0f; m_userData = bd->userData; m_fixtureList = NULL; m_fixtureCount = 0; } b2Body::~b2Body() { // shapes and joints are destroyed in b2World::Destroy } void b2Body::SetType(b2BodyType type) { b2Assert(m_world->IsLocked() == false); if (m_world->IsLocked() == true) { return; } if (m_type == type) { return; } m_type = type; ResetMassData(); if (m_type == b2_staticBody) { m_linearVelocity.SetZero(); m_angularVelocity = 0.0f; m_sweep.a0 = m_sweep.a; m_sweep.c0 = m_sweep.c; SynchronizeFixtures(); } SetAwake(true); m_force.SetZero(); m_torque = 0.0f; // Delete the attached contacts. b2ContactEdge* ce = m_contactList; while (ce) { b2ContactEdge* ce0 = ce; ce = ce->next; m_world->m_contactManager.Destroy(ce0->contact); } m_contactList = NULL; // Touch the proxies so that new contacts will be created (when appropriate) b2BroadPhase* broadPhase = &m_world->m_contactManager.m_broadPhase; for (b2Fixture* f = m_fixtureList; f; f = f->m_next) { int32 proxyCount = f->m_proxyCount; for (int32 i = 0; i < proxyCount; ++i) { broadPhase->TouchProxy(f->m_proxies[i].proxyId); } } } b2Fixture* b2Body::CreateFixture(const b2FixtureDef* def) { b2Assert(m_world->IsLocked() == false); if (m_world->IsLocked() == true) { return NULL; } b2BlockAllocator* allocator = &m_world->m_blockAllocator; void* memory = allocator->Allocate(sizeof(b2Fixture)); b2Fixture* fixture = new (memory) b2Fixture; fixture->Create(allocator, this, def); if (m_flags & e_activeFlag) { b2BroadPhase* broadPhase = &m_world->m_contactManager.m_broadPhase; fixture->CreateProxies(broadPhase, m_xf); } fixture->m_next = m_fixtureList; m_fixtureList = fixture; ++m_fixtureCount; fixture->m_body = this; // Adjust mass properties if needed. if (fixture->m_density > 0.0f) { ResetMassData(); } // Let the world know we have a new fixture. This will cause new contacts // to be created at the beginning of the next time step. m_world->m_flags |= b2World::e_newFixture; return fixture; } b2Fixture* b2Body::CreateFixture(const b2Shape* shape, float32 density) { b2FixtureDef def; def.shape = shape; def.density = density; return CreateFixture(&def); } void b2Body::DestroyFixture(b2Fixture* fixture) { b2Assert(m_world->IsLocked() == false); if (m_world->IsLocked() == true) { return; } b2Assert(fixture->m_body == this); // Remove the fixture from this body's singly linked list. b2Assert(m_fixtureCount > 0); b2Fixture** node = &m_fixtureList; bool found = false; while (*node != NULL) { if (*node == fixture) { *node = fixture->m_next; found = true; break; } node = &(*node)->m_next; } // You tried to remove a shape that is not attached to this body. b2Assert(found); // Destroy any contacts associated with the fixture. b2ContactEdge* edge = m_contactList; while (edge) { b2Contact* c = edge->contact; edge = edge->next; b2Fixture* fixtureA = c->GetFixtureA(); b2Fixture* fixtureB = c->GetFixtureB(); if (fixture == fixtureA || fixture == fixtureB) { // This destroys the contact and removes it from // this body's contact list. m_world->m_contactManager.Destroy(c); } } b2BlockAllocator* allocator = &m_world->m_blockAllocator; if (m_flags & e_activeFlag) { b2BroadPhase* broadPhase = &m_world->m_contactManager.m_broadPhase; fixture->DestroyProxies(broadPhase); } fixture->Destroy(allocator); fixture->m_body = NULL; fixture->m_next = NULL; fixture->~b2Fixture(); allocator->Free(fixture, sizeof(b2Fixture)); --m_fixtureCount; // Reset the mass data. ResetMassData(); } void b2Body::ResetMassData() { // Compute mass data from shapes. Each shape has its own density. m_mass = 0.0f; m_invMass = 0.0f; m_I = 0.0f; m_invI = 0.0f; m_sweep.localCenter.SetZero(); // Static and kinematic bodies have zero mass. if (m_type == b2_staticBody || m_type == b2_kinematicBody) { m_sweep.c0 = m_xf.p; m_sweep.c = m_xf.p; m_sweep.a0 = m_sweep.a; return; } b2Assert(m_type == b2_dynamicBody); // Accumulate mass over all fixtures. b2Vec2 localCenter = b2Vec2_zero; for (b2Fixture* f = m_fixtureList; f; f = f->m_next) { if (f->m_density == 0.0f) { continue; } b2MassData massData; f->GetMassData(&massData); m_mass += massData.mass; localCenter += massData.mass * massData.center; m_I += massData.I; } // Compute center of mass. if (m_mass > 0.0f) { m_invMass = 1.0f / m_mass; localCenter *= m_invMass; } else { // Force all dynamic bodies to have a positive mass. m_mass = 1.0f; m_invMass = 1.0f; } if (m_I > 0.0f && (m_flags & e_fixedRotationFlag) == 0) { // Center the inertia about the center of mass. m_I -= m_mass * b2Dot(localCenter, localCenter); b2Assert(m_I > 0.0f); m_invI = 1.0f / m_I; } else { m_I = 0.0f; m_invI = 0.0f; } // Move center of mass. b2Vec2 oldCenter = m_sweep.c; m_sweep.localCenter = localCenter; m_sweep.c0 = m_sweep.c = b2Mul(m_xf, m_sweep.localCenter); // Update center of mass velocity. m_linearVelocity += b2Cross(m_angularVelocity, m_sweep.c - oldCenter); } void b2Body::SetMassData(const b2MassData* massData) { b2Assert(m_world->IsLocked() == false); if (m_world->IsLocked() == true) { return; } if (m_type != b2_dynamicBody) { return; } m_invMass = 0.0f; m_I = 0.0f; m_invI = 0.0f; m_mass = massData->mass; if (m_mass <= 0.0f) { m_mass = 1.0f; } m_invMass = 1.0f / m_mass; if (massData->I > 0.0f && (m_flags & b2Body::e_fixedRotationFlag) == 0) { m_I = massData->I - m_mass * b2Dot(massData->center, massData->center); b2Assert(m_I > 0.0f); m_invI = 1.0f / m_I; } // Move center of mass. b2Vec2 oldCenter = m_sweep.c; m_sweep.localCenter = massData->center; m_sweep.c0 = m_sweep.c = b2Mul(m_xf, m_sweep.localCenter); // Update center of mass velocity. m_linearVelocity += b2Cross(m_angularVelocity, m_sweep.c - oldCenter); } bool b2Body::ShouldCollide(const b2Body* other) const { // At least one body should be dynamic. if (m_type != b2_dynamicBody && other->m_type != b2_dynamicBody) { return false; } // Does a joint prevent collision? for (b2JointEdge* jn = m_jointList; jn; jn = jn->next) { if (jn->other == other) { if (jn->joint->m_collideConnected == false) { return false; } } } return true; } void b2Body::SetTransform(const b2Vec2& position, float32 angle) { b2Assert(m_world->IsLocked() == false); if (m_world->IsLocked() == true) { return; } m_xf.q.Set(angle); m_xf.p = position; m_sweep.c = b2Mul(m_xf, m_sweep.localCenter); m_sweep.a = angle; m_sweep.c0 = m_sweep.c; m_sweep.a0 = angle; b2BroadPhase* broadPhase = &m_world->m_contactManager.m_broadPhase; for (b2Fixture* f = m_fixtureList; f; f = f->m_next) { f->Synchronize(broadPhase, m_xf, m_xf); } } void b2Body::SynchronizeFixtures() { b2Transform xf1; xf1.q.Set(m_sweep.a0); xf1.p = m_sweep.c0 - b2Mul(xf1.q, m_sweep.localCenter); b2BroadPhase* broadPhase = &m_world->m_contactManager.m_broadPhase; for (b2Fixture* f = m_fixtureList; f; f = f->m_next) { f->Synchronize(broadPhase, xf1, m_xf); } } void b2Body::SetActive(bool flag) { b2Assert(m_world->IsLocked() == false); if (flag == IsActive()) { return; } if (flag) { m_flags |= e_activeFlag; // Create all proxies. b2BroadPhase* broadPhase = &m_world->m_contactManager.m_broadPhase; for (b2Fixture* f = m_fixtureList; f; f = f->m_next) { f->CreateProxies(broadPhase, m_xf); } // Contacts are created the next time step. } else { m_flags &= ~e_activeFlag; // Destroy all proxies. b2BroadPhase* broadPhase = &m_world->m_contactManager.m_broadPhase; for (b2Fixture* f = m_fixtureList; f; f = f->m_next) { f->DestroyProxies(broadPhase); } // Destroy the attached contacts. b2ContactEdge* ce = m_contactList; while (ce) { b2ContactEdge* ce0 = ce; ce = ce->next; m_world->m_contactManager.Destroy(ce0->contact); } m_contactList = NULL; } } void b2Body::SetFixedRotation(bool flag) { bool status = (m_flags & e_fixedRotationFlag) == e_fixedRotationFlag; if (status == flag) { return; } if (flag) { m_flags |= e_fixedRotationFlag; } else { m_flags &= ~e_fixedRotationFlag; } m_angularVelocity = 0.0f; ResetMassData(); } void b2Body::Dump() { int32 bodyIndex = m_islandIndex; b2Log("{\n"); b2Log(" b2BodyDef bd;\n"); b2Log(" bd.type = b2BodyType(%d);\n", m_type); b2Log(" bd.position.Set(%.15lef, %.15lef);\n", m_xf.p.x, m_xf.p.y); b2Log(" bd.angle = %.15lef;\n", m_sweep.a); b2Log(" bd.linearVelocity.Set(%.15lef, %.15lef);\n", m_linearVelocity.x, m_linearVelocity.y); b2Log(" bd.angularVelocity = %.15lef;\n", m_angularVelocity); b2Log(" bd.linearDamping = %.15lef;\n", m_linearDamping); b2Log(" bd.angularDamping = %.15lef;\n", m_angularDamping); b2Log(" bd.allowSleep = bool(%d);\n", m_flags & e_autoSleepFlag); b2Log(" bd.awake = bool(%d);\n", m_flags & e_awakeFlag); b2Log(" bd.fixedRotation = bool(%d);\n", m_flags & e_fixedRotationFlag); b2Log(" bd.bullet = bool(%d);\n", m_flags & e_bulletFlag); b2Log(" bd.active = bool(%d);\n", m_flags & e_activeFlag); b2Log(" bd.gravityScale = %.15lef;\n", m_gravityScale); b2Log(" bodies[%d] = m_world->CreateBody(&bd);\n", m_islandIndex); b2Log("\n"); for (b2Fixture* f = m_fixtureList; f; f = f->m_next) { b2Log(" {\n"); f->Dump(bodyIndex); b2Log(" }\n"); } b2Log("}\n"); } ================================================ FILE: app/src/main/cpp/b2BroadPhase.cpp ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Collision/b2BroadPhase.h" b2BroadPhase::b2BroadPhase() { m_proxyCount = 0; m_pairCapacity = 16; m_pairCount = 0; m_pairBuffer = (b2Pair*)b2Alloc(m_pairCapacity * sizeof(b2Pair)); m_moveCapacity = 16; m_moveCount = 0; m_moveBuffer = (int32*)b2Alloc(m_moveCapacity * sizeof(int32)); } b2BroadPhase::~b2BroadPhase() { b2Free(m_moveBuffer); b2Free(m_pairBuffer); } int32 b2BroadPhase::CreateProxy(const b2AABB& aabb, void* userData) { int32 proxyId = m_tree.CreateProxy(aabb, userData); ++m_proxyCount; BufferMove(proxyId); return proxyId; } void b2BroadPhase::DestroyProxy(int32 proxyId) { UnBufferMove(proxyId); --m_proxyCount; m_tree.DestroyProxy(proxyId); } void b2BroadPhase::MoveProxy(int32 proxyId, const b2AABB& aabb, const b2Vec2& displacement) { bool buffer = m_tree.MoveProxy(proxyId, aabb, displacement); if (buffer) { BufferMove(proxyId); } } void b2BroadPhase::TouchProxy(int32 proxyId) { BufferMove(proxyId); } void b2BroadPhase::BufferMove(int32 proxyId) { if (m_moveCount == m_moveCapacity) { int32* oldBuffer = m_moveBuffer; m_moveCapacity *= 2; m_moveBuffer = (int32*)b2Alloc(m_moveCapacity * sizeof(int32)); memcpy(m_moveBuffer, oldBuffer, m_moveCount * sizeof(int32)); b2Free(oldBuffer); } m_moveBuffer[m_moveCount] = proxyId; ++m_moveCount; } void b2BroadPhase::UnBufferMove(int32 proxyId) { for (int32 i = 0; i < m_moveCount; ++i) { if (m_moveBuffer[i] == proxyId) { m_moveBuffer[i] = e_nullProxy; } } } // This is called from b2DynamicTree::Query when we are gathering pairs. bool b2BroadPhase::QueryCallback(int32 proxyId) { // A proxy cannot form a pair with itself. if (proxyId == m_queryProxyId) { return true; } // Grow the pair buffer as needed. if (m_pairCount == m_pairCapacity) { b2Pair* oldBuffer = m_pairBuffer; m_pairCapacity *= 2; m_pairBuffer = (b2Pair*)b2Alloc(m_pairCapacity * sizeof(b2Pair)); memcpy(m_pairBuffer, oldBuffer, m_pairCount * sizeof(b2Pair)); b2Free(oldBuffer); } m_pairBuffer[m_pairCount].proxyIdA = b2Min(proxyId, m_queryProxyId); m_pairBuffer[m_pairCount].proxyIdB = b2Max(proxyId, m_queryProxyId); ++m_pairCount; return true; } ================================================ FILE: app/src/main/cpp/b2ChainAndCircleContact.cpp ================================================ /* * Copyright (c) 2006-2010 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Dynamics/Contacts/b2ChainAndCircleContact.h" #include "isEngine/ext_lib/Box2D/Common/b2BlockAllocator.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Fixture.h" #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2ChainShape.h" #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2EdgeShape.h" #include b2Contact* b2ChainAndCircleContact::Create(b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB, b2BlockAllocator* allocator) { void* mem = allocator->Allocate(sizeof(b2ChainAndCircleContact)); return new (mem) b2ChainAndCircleContact(fixtureA, indexA, fixtureB, indexB); } void b2ChainAndCircleContact::Destroy(b2Contact* contact, b2BlockAllocator* allocator) { ((b2ChainAndCircleContact*)contact)->~b2ChainAndCircleContact(); allocator->Free(contact, sizeof(b2ChainAndCircleContact)); } b2ChainAndCircleContact::b2ChainAndCircleContact(b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB) : b2Contact(fixtureA, indexA, fixtureB, indexB) { b2Assert(m_fixtureA->GetType() == b2Shape::e_chain); b2Assert(m_fixtureB->GetType() == b2Shape::e_circle); } void b2ChainAndCircleContact::Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB) { b2ChainShape* chain = (b2ChainShape*)m_fixtureA->GetShape(); b2EdgeShape edge; chain->GetChildEdge(&edge, m_indexA); b2CollideEdgeAndCircle( manifold, &edge, xfA, (b2CircleShape*)m_fixtureB->GetShape(), xfB); } ================================================ FILE: app/src/main/cpp/b2ChainAndPolygonContact.cpp ================================================ /* * Copyright (c) 2006-2010 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Dynamics/Contacts/b2ChainAndPolygonContact.h" #include "isEngine/ext_lib/Box2D/Common/b2BlockAllocator.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Fixture.h" #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2ChainShape.h" #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2EdgeShape.h" #include b2Contact* b2ChainAndPolygonContact::Create(b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB, b2BlockAllocator* allocator) { void* mem = allocator->Allocate(sizeof(b2ChainAndPolygonContact)); return new (mem) b2ChainAndPolygonContact(fixtureA, indexA, fixtureB, indexB); } void b2ChainAndPolygonContact::Destroy(b2Contact* contact, b2BlockAllocator* allocator) { ((b2ChainAndPolygonContact*)contact)->~b2ChainAndPolygonContact(); allocator->Free(contact, sizeof(b2ChainAndPolygonContact)); } b2ChainAndPolygonContact::b2ChainAndPolygonContact(b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB) : b2Contact(fixtureA, indexA, fixtureB, indexB) { b2Assert(m_fixtureA->GetType() == b2Shape::e_chain); b2Assert(m_fixtureB->GetType() == b2Shape::e_polygon); } void b2ChainAndPolygonContact::Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB) { b2ChainShape* chain = (b2ChainShape*)m_fixtureA->GetShape(); b2EdgeShape edge; chain->GetChildEdge(&edge, m_indexA); b2CollideEdgeAndPolygon( manifold, &edge, xfA, (b2PolygonShape*)m_fixtureB->GetShape(), xfB); } ================================================ FILE: app/src/main/cpp/b2ChainShape.cpp ================================================ /* * Copyright (c) 2006-2010 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2ChainShape.h" #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2EdgeShape.h" #include #include #include b2ChainShape::~b2ChainShape() { b2Free(m_vertices); m_vertices = NULL; m_count = 0; } void b2ChainShape::CreateLoop(const b2Vec2* vertices, int32 count) { b2Assert(m_vertices == NULL && m_count == 0); b2Assert(count >= 3); for (int32 i = 1; i < count; ++i) { b2Vec2 v1 = vertices[i-1]; b2Vec2 v2 = vertices[i]; // If the code crashes here, it means your vertices are too close together. b2Assert(b2DistanceSquared(v1, v2) > b2_linearSlop * b2_linearSlop); } m_count = count + 1; m_vertices = (b2Vec2*)b2Alloc(m_count * sizeof(b2Vec2)); memcpy(m_vertices, vertices, count * sizeof(b2Vec2)); m_vertices[count] = m_vertices[0]; m_prevVertex = m_vertices[m_count - 2]; m_nextVertex = m_vertices[1]; m_hasPrevVertex = true; m_hasNextVertex = true; } void b2ChainShape::CreateChain(const b2Vec2* vertices, int32 count) { b2Assert(m_vertices == NULL && m_count == 0); b2Assert(count >= 2); for (int32 i = 1; i < count; ++i) { b2Vec2 v1 = vertices[i-1]; b2Vec2 v2 = vertices[i]; // If the code crashes here, it means your vertices are too close together. b2Assert(b2DistanceSquared(v1, v2) > b2_linearSlop * b2_linearSlop); } m_count = count; m_vertices = (b2Vec2*)b2Alloc(count * sizeof(b2Vec2)); memcpy(m_vertices, vertices, m_count * sizeof(b2Vec2)); m_hasPrevVertex = false; m_hasNextVertex = false; m_prevVertex.SetZero(); m_nextVertex.SetZero(); } void b2ChainShape::SetPrevVertex(const b2Vec2& prevVertex) { m_prevVertex = prevVertex; m_hasPrevVertex = true; } void b2ChainShape::SetNextVertex(const b2Vec2& nextVertex) { m_nextVertex = nextVertex; m_hasNextVertex = true; } b2Shape* b2ChainShape::Clone(b2BlockAllocator* allocator) const { void* mem = allocator->Allocate(sizeof(b2ChainShape)); b2ChainShape* clone = new (mem) b2ChainShape; clone->CreateChain(m_vertices, m_count); clone->m_prevVertex = m_prevVertex; clone->m_nextVertex = m_nextVertex; clone->m_hasPrevVertex = m_hasPrevVertex; clone->m_hasNextVertex = m_hasNextVertex; return clone; } int32 b2ChainShape::GetChildCount() const { // edge count = vertex count - 1 return m_count - 1; } void b2ChainShape::GetChildEdge(b2EdgeShape* edge, int32 index) const { b2Assert(0 <= index && index < m_count - 1); edge->m_type = b2Shape::e_edge; edge->m_radius = m_radius; edge->m_vertex1 = m_vertices[index + 0]; edge->m_vertex2 = m_vertices[index + 1]; if (index > 0) { edge->m_vertex0 = m_vertices[index - 1]; edge->m_hasVertex0 = true; } else { edge->m_vertex0 = m_prevVertex; edge->m_hasVertex0 = m_hasPrevVertex; } if (index < m_count - 2) { edge->m_vertex3 = m_vertices[index + 2]; edge->m_hasVertex3 = true; } else { edge->m_vertex3 = m_nextVertex; edge->m_hasVertex3 = m_hasNextVertex; } } bool b2ChainShape::TestPoint(const b2Transform& xf, const b2Vec2& p) const { B2_NOT_USED(xf); B2_NOT_USED(p); return false; } bool b2ChainShape::RayCast(b2RayCastOutput* output, const b2RayCastInput& input, const b2Transform& xf, int32 childIndex) const { b2Assert(childIndex < m_count); b2EdgeShape edgeShape; int32 i1 = childIndex; int32 i2 = childIndex + 1; if (i2 == m_count) { i2 = 0; } edgeShape.m_vertex1 = m_vertices[i1]; edgeShape.m_vertex2 = m_vertices[i2]; return edgeShape.RayCast(output, input, xf, 0); } void b2ChainShape::ComputeAABB(b2AABB* aabb, const b2Transform& xf, int32 childIndex) const { b2Assert(childIndex < m_count); int32 i1 = childIndex; int32 i2 = childIndex + 1; if (i2 == m_count) { i2 = 0; } b2Vec2 v1 = b2Mul(xf, m_vertices[i1]); b2Vec2 v2 = b2Mul(xf, m_vertices[i2]); aabb->lowerBound = b2Min(v1, v2); aabb->upperBound = b2Max(v1, v2); } void b2ChainShape::ComputeMass(b2MassData* massData, float32 density) const { B2_NOT_USED(density); massData->mass = 0.0f; massData->center.SetZero(); massData->I = 0.0f; } ================================================ FILE: app/src/main/cpp/b2CircleContact.cpp ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Dynamics/Contacts/b2CircleContact.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Body.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Fixture.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2WorldCallbacks.h" #include "isEngine/ext_lib/Box2D/Common/b2BlockAllocator.h" #include "isEngine/ext_lib/Box2D/Collision/b2TimeOfImpact.h" #include b2Contact* b2CircleContact::Create(b2Fixture* fixtureA, int32, b2Fixture* fixtureB, int32, b2BlockAllocator* allocator) { void* mem = allocator->Allocate(sizeof(b2CircleContact)); return new (mem) b2CircleContact(fixtureA, fixtureB); } void b2CircleContact::Destroy(b2Contact* contact, b2BlockAllocator* allocator) { ((b2CircleContact*)contact)->~b2CircleContact(); allocator->Free(contact, sizeof(b2CircleContact)); } b2CircleContact::b2CircleContact(b2Fixture* fixtureA, b2Fixture* fixtureB) : b2Contact(fixtureA, 0, fixtureB, 0) { b2Assert(m_fixtureA->GetType() == b2Shape::e_circle); b2Assert(m_fixtureB->GetType() == b2Shape::e_circle); } void b2CircleContact::Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB) { b2CollideCircles(manifold, (b2CircleShape*)m_fixtureA->GetShape(), xfA, (b2CircleShape*)m_fixtureB->GetShape(), xfB); } ================================================ FILE: app/src/main/cpp/b2CircleShape.cpp ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2CircleShape.h" #include b2Shape* b2CircleShape::Clone(b2BlockAllocator* allocator) const { void* mem = allocator->Allocate(sizeof(b2CircleShape)); b2CircleShape* clone = new (mem) b2CircleShape; *clone = *this; return clone; } int32 b2CircleShape::GetChildCount() const { return 1; } bool b2CircleShape::TestPoint(const b2Transform& transform, const b2Vec2& p) const { b2Vec2 center = transform.p + b2Mul(transform.q, m_p); b2Vec2 d = p - center; return b2Dot(d, d) <= m_radius * m_radius; } // Collision Detection in Interactive 3D Environments by Gino van den Bergen // From Section 3.1.2 // x = s + a * r // norm(x) = radius bool b2CircleShape::RayCast(b2RayCastOutput* output, const b2RayCastInput& input, const b2Transform& transform, int32 childIndex) const { B2_NOT_USED(childIndex); b2Vec2 position = transform.p + b2Mul(transform.q, m_p); b2Vec2 s = input.p1 - position; float32 b = b2Dot(s, s) - m_radius * m_radius; // Solve quadratic equation. b2Vec2 r = input.p2 - input.p1; float32 c = b2Dot(s, r); float32 rr = b2Dot(r, r); float32 sigma = c * c - rr * b; // Check for negative discriminant and short segment. if (sigma < 0.0f || rr < b2_epsilon) { return false; } // Find the point of intersection of the line with the circle. float32 a = -(c + b2Sqrt(sigma)); // Is the intersection point on the segment? if (0.0f <= a && a <= input.maxFraction * rr) { a /= rr; output->fraction = a; output->normal = s + a * r; output->normal.Normalize(); return true; } return false; } void b2CircleShape::ComputeAABB(b2AABB* aabb, const b2Transform& transform, int32 childIndex) const { B2_NOT_USED(childIndex); b2Vec2 p = transform.p + b2Mul(transform.q, m_p); aabb->lowerBound.Set(p.x - m_radius, p.y - m_radius); aabb->upperBound.Set(p.x + m_radius, p.y + m_radius); } void b2CircleShape::ComputeMass(b2MassData* massData, float32 density) const { massData->mass = density * b2_pi * m_radius * m_radius; massData->center = m_p; // inertia about the local origin massData->I = massData->mass * (0.5f * m_radius * m_radius + b2Dot(m_p, m_p)); } ================================================ FILE: app/src/main/cpp/b2CollideCircle.cpp ================================================ /* * Copyright (c) 2007-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Collision/b2Collision.h" #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2CircleShape.h" #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2PolygonShape.h" void b2CollideCircles( b2Manifold* manifold, const b2CircleShape* circleA, const b2Transform& xfA, const b2CircleShape* circleB, const b2Transform& xfB) { manifold->pointCount = 0; b2Vec2 pA = b2Mul(xfA, circleA->m_p); b2Vec2 pB = b2Mul(xfB, circleB->m_p); b2Vec2 d = pB - pA; float32 distSqr = b2Dot(d, d); float32 rA = circleA->m_radius, rB = circleB->m_radius; float32 radius = rA + rB; if (distSqr > radius * radius) { return; } manifold->type = b2Manifold::e_circles; manifold->localPoint = circleA->m_p; manifold->localNormal.SetZero(); manifold->pointCount = 1; manifold->points[0].localPoint = circleB->m_p; manifold->points[0].id.key = 0; } void b2CollidePolygonAndCircle( b2Manifold* manifold, const b2PolygonShape* polygonA, const b2Transform& xfA, const b2CircleShape* circleB, const b2Transform& xfB) { manifold->pointCount = 0; // Compute circle position in the frame of the polygon. b2Vec2 c = b2Mul(xfB, circleB->m_p); b2Vec2 cLocal = b2MulT(xfA, c); // Find the min separating edge. int32 normalIndex = 0; float32 separation = -b2_maxFloat; float32 radius = polygonA->m_radius + circleB->m_radius; int32 vertexCount = polygonA->m_count; const b2Vec2* vertices = polygonA->m_vertices; const b2Vec2* normals = polygonA->m_normals; for (int32 i = 0; i < vertexCount; ++i) { float32 s = b2Dot(normals[i], cLocal - vertices[i]); if (s > radius) { // Early out. return; } if (s > separation) { separation = s; normalIndex = i; } } // Vertices that subtend the incident face. int32 vertIndex1 = normalIndex; int32 vertIndex2 = vertIndex1 + 1 < vertexCount ? vertIndex1 + 1 : 0; b2Vec2 v1 = vertices[vertIndex1]; b2Vec2 v2 = vertices[vertIndex2]; // If the center is inside the polygon ... if (separation < b2_epsilon) { manifold->pointCount = 1; manifold->type = b2Manifold::e_faceA; manifold->localNormal = normals[normalIndex]; manifold->localPoint = 0.5f * (v1 + v2); manifold->points[0].localPoint = circleB->m_p; manifold->points[0].id.key = 0; return; } // Compute barycentric coordinates float32 u1 = b2Dot(cLocal - v1, v2 - v1); float32 u2 = b2Dot(cLocal - v2, v1 - v2); if (u1 <= 0.0f) { if (b2DistanceSquared(cLocal, v1) > radius * radius) { return; } manifold->pointCount = 1; manifold->type = b2Manifold::e_faceA; manifold->localNormal = cLocal - v1; manifold->localNormal.Normalize(); manifold->localPoint = v1; manifold->points[0].localPoint = circleB->m_p; manifold->points[0].id.key = 0; } else if (u2 <= 0.0f) { if (b2DistanceSquared(cLocal, v2) > radius * radius) { return; } manifold->pointCount = 1; manifold->type = b2Manifold::e_faceA; manifold->localNormal = cLocal - v2; manifold->localNormal.Normalize(); manifold->localPoint = v2; manifold->points[0].localPoint = circleB->m_p; manifold->points[0].id.key = 0; } else { b2Vec2 faceCenter = 0.5f * (v1 + v2); float32 separation = b2Dot(cLocal - faceCenter, normals[vertIndex1]); if (separation > radius) { return; } manifold->pointCount = 1; manifold->type = b2Manifold::e_faceA; manifold->localNormal = normals[vertIndex1]; manifold->localPoint = faceCenter; manifold->points[0].localPoint = circleB->m_p; manifold->points[0].id.key = 0; } } ================================================ FILE: app/src/main/cpp/b2CollideEdge.cpp ================================================ /* * Copyright (c) 2007-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Collision/b2Collision.h" #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2CircleShape.h" #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2EdgeShape.h" #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2PolygonShape.h" // Compute contact points for edge versus circle. // This accounts for edge connectivity. void b2CollideEdgeAndCircle(b2Manifold* manifold, const b2EdgeShape* edgeA, const b2Transform& xfA, const b2CircleShape* circleB, const b2Transform& xfB) { manifold->pointCount = 0; // Compute circle in frame of edge b2Vec2 Q = b2MulT(xfA, b2Mul(xfB, circleB->m_p)); b2Vec2 A = edgeA->m_vertex1, B = edgeA->m_vertex2; b2Vec2 e = B - A; // Barycentric coordinates float32 u = b2Dot(e, B - Q); float32 v = b2Dot(e, Q - A); float32 radius = edgeA->m_radius + circleB->m_radius; b2ContactFeature cf; cf.indexB = 0; cf.typeB = b2ContactFeature::e_vertex; // Region A if (v <= 0.0f) { b2Vec2 P = A; b2Vec2 d = Q - P; float32 dd = b2Dot(d, d); if (dd > radius * radius) { return; } // Is there an edge connected to A? if (edgeA->m_hasVertex0) { b2Vec2 A1 = edgeA->m_vertex0; b2Vec2 B1 = A; b2Vec2 e1 = B1 - A1; float32 u1 = b2Dot(e1, B1 - Q); // Is the circle in Region AB of the previous edge? if (u1 > 0.0f) { return; } } cf.indexA = 0; cf.typeA = b2ContactFeature::e_vertex; manifold->pointCount = 1; manifold->type = b2Manifold::e_circles; manifold->localNormal.SetZero(); manifold->localPoint = P; manifold->points[0].id.key = 0; manifold->points[0].id.cf = cf; manifold->points[0].localPoint = circleB->m_p; return; } // Region B if (u <= 0.0f) { b2Vec2 P = B; b2Vec2 d = Q - P; float32 dd = b2Dot(d, d); if (dd > radius * radius) { return; } // Is there an edge connected to B? if (edgeA->m_hasVertex3) { b2Vec2 B2 = edgeA->m_vertex3; b2Vec2 A2 = B; b2Vec2 e2 = B2 - A2; float32 v2 = b2Dot(e2, Q - A2); // Is the circle in Region AB of the next edge? if (v2 > 0.0f) { return; } } cf.indexA = 1; cf.typeA = b2ContactFeature::e_vertex; manifold->pointCount = 1; manifold->type = b2Manifold::e_circles; manifold->localNormal.SetZero(); manifold->localPoint = P; manifold->points[0].id.key = 0; manifold->points[0].id.cf = cf; manifold->points[0].localPoint = circleB->m_p; return; } // Region AB float32 den = b2Dot(e, e); b2Assert(den > 0.0f); b2Vec2 P = (1.0f / den) * (u * A + v * B); b2Vec2 d = Q - P; float32 dd = b2Dot(d, d); if (dd > radius * radius) { return; } b2Vec2 n(-e.y, e.x); if (b2Dot(n, Q - A) < 0.0f) { n.Set(-n.x, -n.y); } n.Normalize(); cf.indexA = 0; cf.typeA = b2ContactFeature::e_face; manifold->pointCount = 1; manifold->type = b2Manifold::e_faceA; manifold->localNormal = n; manifold->localPoint = A; manifold->points[0].id.key = 0; manifold->points[0].id.cf = cf; manifold->points[0].localPoint = circleB->m_p; } // This structure is used to keep track of the best separating axis. struct b2EPAxis { enum Type { e_unknown, e_edgeA, e_edgeB }; Type type; int32 index; float32 separation; }; // This holds polygon B expressed in frame A. struct b2TempPolygon { b2Vec2 vertices[b2_maxPolygonVertices]; b2Vec2 normals[b2_maxPolygonVertices]; int32 count; }; // Reference face used for clipping struct b2ReferenceFace { int32 i1, i2; b2Vec2 v1, v2; b2Vec2 normal; b2Vec2 sideNormal1; float32 sideOffset1; b2Vec2 sideNormal2; float32 sideOffset2; }; // This class collides and edge and a polygon, taking into account edge adjacency. struct b2EPCollider { void Collide(b2Manifold* manifold, const b2EdgeShape* edgeA, const b2Transform& xfA, const b2PolygonShape* polygonB, const b2Transform& xfB); b2EPAxis ComputeEdgeSeparation(); b2EPAxis ComputePolygonSeparation(); enum VertexType { e_isolated, e_concave, e_convex }; b2TempPolygon m_polygonB; b2Transform m_xf; b2Vec2 m_centroidB; b2Vec2 m_v0, m_v1, m_v2, m_v3; b2Vec2 m_normal0, m_normal1, m_normal2; b2Vec2 m_normal; VertexType m_type1, m_type2; b2Vec2 m_lowerLimit, m_upperLimit; float32 m_radius; bool m_front; }; // Algorithm: // 1. Classify v1 and v2 // 2. Classify polygon centroid as front or back // 3. Flip normal if necessary // 4. Initialize normal range to [-pi, pi] about face normal // 5. Adjust normal range according to adjacent edges // 6. Visit each separating axes, only accept axes within the range // 7. Return if _any_ axis indicates separation // 8. Clip void b2EPCollider::Collide(b2Manifold* manifold, const b2EdgeShape* edgeA, const b2Transform& xfA, const b2PolygonShape* polygonB, const b2Transform& xfB) { m_xf = b2MulT(xfA, xfB); m_centroidB = b2Mul(m_xf, polygonB->m_centroid); m_v0 = edgeA->m_vertex0; m_v1 = edgeA->m_vertex1; m_v2 = edgeA->m_vertex2; m_v3 = edgeA->m_vertex3; bool hasVertex0 = edgeA->m_hasVertex0; bool hasVertex3 = edgeA->m_hasVertex3; b2Vec2 edge1 = m_v2 - m_v1; edge1.Normalize(); m_normal1.Set(edge1.y, -edge1.x); float32 offset1 = b2Dot(m_normal1, m_centroidB - m_v1); float32 offset0 = 0.0f, offset2 = 0.0f; bool convex1 = false, convex2 = false; // Is there a preceding edge? if (hasVertex0) { b2Vec2 edge0 = m_v1 - m_v0; edge0.Normalize(); m_normal0.Set(edge0.y, -edge0.x); convex1 = b2Cross(edge0, edge1) >= 0.0f; offset0 = b2Dot(m_normal0, m_centroidB - m_v0); } // Is there a following edge? if (hasVertex3) { b2Vec2 edge2 = m_v3 - m_v2; edge2.Normalize(); m_normal2.Set(edge2.y, -edge2.x); convex2 = b2Cross(edge1, edge2) > 0.0f; offset2 = b2Dot(m_normal2, m_centroidB - m_v2); } // Determine front or back collision. Determine collision normal limits. if (hasVertex0 && hasVertex3) { if (convex1 && convex2) { m_front = offset0 >= 0.0f || offset1 >= 0.0f || offset2 >= 0.0f; if (m_front) { m_normal = m_normal1; m_lowerLimit = m_normal0; m_upperLimit = m_normal2; } else { m_normal = -m_normal1; m_lowerLimit = -m_normal1; m_upperLimit = -m_normal1; } } else if (convex1) { m_front = offset0 >= 0.0f || (offset1 >= 0.0f && offset2 >= 0.0f); if (m_front) { m_normal = m_normal1; m_lowerLimit = m_normal0; m_upperLimit = m_normal1; } else { m_normal = -m_normal1; m_lowerLimit = -m_normal2; m_upperLimit = -m_normal1; } } else if (convex2) { m_front = offset2 >= 0.0f || (offset0 >= 0.0f && offset1 >= 0.0f); if (m_front) { m_normal = m_normal1; m_lowerLimit = m_normal1; m_upperLimit = m_normal2; } else { m_normal = -m_normal1; m_lowerLimit = -m_normal1; m_upperLimit = -m_normal0; } } else { m_front = offset0 >= 0.0f && offset1 >= 0.0f && offset2 >= 0.0f; if (m_front) { m_normal = m_normal1; m_lowerLimit = m_normal1; m_upperLimit = m_normal1; } else { m_normal = -m_normal1; m_lowerLimit = -m_normal2; m_upperLimit = -m_normal0; } } } else if (hasVertex0) { if (convex1) { m_front = offset0 >= 0.0f || offset1 >= 0.0f; if (m_front) { m_normal = m_normal1; m_lowerLimit = m_normal0; m_upperLimit = -m_normal1; } else { m_normal = -m_normal1; m_lowerLimit = m_normal1; m_upperLimit = -m_normal1; } } else { m_front = offset0 >= 0.0f && offset1 >= 0.0f; if (m_front) { m_normal = m_normal1; m_lowerLimit = m_normal1; m_upperLimit = -m_normal1; } else { m_normal = -m_normal1; m_lowerLimit = m_normal1; m_upperLimit = -m_normal0; } } } else if (hasVertex3) { if (convex2) { m_front = offset1 >= 0.0f || offset2 >= 0.0f; if (m_front) { m_normal = m_normal1; m_lowerLimit = -m_normal1; m_upperLimit = m_normal2; } else { m_normal = -m_normal1; m_lowerLimit = -m_normal1; m_upperLimit = m_normal1; } } else { m_front = offset1 >= 0.0f && offset2 >= 0.0f; if (m_front) { m_normal = m_normal1; m_lowerLimit = -m_normal1; m_upperLimit = m_normal1; } else { m_normal = -m_normal1; m_lowerLimit = -m_normal2; m_upperLimit = m_normal1; } } } else { m_front = offset1 >= 0.0f; if (m_front) { m_normal = m_normal1; m_lowerLimit = -m_normal1; m_upperLimit = -m_normal1; } else { m_normal = -m_normal1; m_lowerLimit = m_normal1; m_upperLimit = m_normal1; } } // Get polygonB in frameA m_polygonB.count = polygonB->m_count; for (int32 i = 0; i < polygonB->m_count; ++i) { m_polygonB.vertices[i] = b2Mul(m_xf, polygonB->m_vertices[i]); m_polygonB.normals[i] = b2Mul(m_xf.q, polygonB->m_normals[i]); } m_radius = 2.0f * b2_polygonRadius; manifold->pointCount = 0; b2EPAxis edgeAxis = ComputeEdgeSeparation(); // If no valid normal can be found than this edge should not collide. if (edgeAxis.type == b2EPAxis::e_unknown) { return; } if (edgeAxis.separation > m_radius) { return; } b2EPAxis polygonAxis = ComputePolygonSeparation(); if (polygonAxis.type != b2EPAxis::e_unknown && polygonAxis.separation > m_radius) { return; } // Use hysteresis for jitter reduction. const float32 k_relativeTol = 0.98f; const float32 k_absoluteTol = 0.001f; b2EPAxis primaryAxis; if (polygonAxis.type == b2EPAxis::e_unknown) { primaryAxis = edgeAxis; } else if (polygonAxis.separation > k_relativeTol * edgeAxis.separation + k_absoluteTol) { primaryAxis = polygonAxis; } else { primaryAxis = edgeAxis; } b2ClipVertex ie[2]; b2ReferenceFace rf; if (primaryAxis.type == b2EPAxis::e_edgeA) { manifold->type = b2Manifold::e_faceA; // Search for the polygon normal that is most anti-parallel to the edge normal. int32 bestIndex = 0; float32 bestValue = b2Dot(m_normal, m_polygonB.normals[0]); for (int32 i = 1; i < m_polygonB.count; ++i) { float32 value = b2Dot(m_normal, m_polygonB.normals[i]); if (value < bestValue) { bestValue = value; bestIndex = i; } } int32 i1 = bestIndex; int32 i2 = i1 + 1 < m_polygonB.count ? i1 + 1 : 0; ie[0].v = m_polygonB.vertices[i1]; ie[0].id.cf.indexA = 0; ie[0].id.cf.indexB = static_cast(i1); ie[0].id.cf.typeA = b2ContactFeature::e_face; ie[0].id.cf.typeB = b2ContactFeature::e_vertex; ie[1].v = m_polygonB.vertices[i2]; ie[1].id.cf.indexA = 0; ie[1].id.cf.indexB = static_cast(i2); ie[1].id.cf.typeA = b2ContactFeature::e_face; ie[1].id.cf.typeB = b2ContactFeature::e_vertex; if (m_front) { rf.i1 = 0; rf.i2 = 1; rf.v1 = m_v1; rf.v2 = m_v2; rf.normal = m_normal1; } else { rf.i1 = 1; rf.i2 = 0; rf.v1 = m_v2; rf.v2 = m_v1; rf.normal = -m_normal1; } } else { manifold->type = b2Manifold::e_faceB; ie[0].v = m_v1; ie[0].id.cf.indexA = 0; ie[0].id.cf.indexB = static_cast(primaryAxis.index); ie[0].id.cf.typeA = b2ContactFeature::e_vertex; ie[0].id.cf.typeB = b2ContactFeature::e_face; ie[1].v = m_v2; ie[1].id.cf.indexA = 0; ie[1].id.cf.indexB = static_cast(primaryAxis.index); ie[1].id.cf.typeA = b2ContactFeature::e_vertex; ie[1].id.cf.typeB = b2ContactFeature::e_face; rf.i1 = primaryAxis.index; rf.i2 = rf.i1 + 1 < m_polygonB.count ? rf.i1 + 1 : 0; rf.v1 = m_polygonB.vertices[rf.i1]; rf.v2 = m_polygonB.vertices[rf.i2]; rf.normal = m_polygonB.normals[rf.i1]; } rf.sideNormal1.Set(rf.normal.y, -rf.normal.x); rf.sideNormal2 = -rf.sideNormal1; rf.sideOffset1 = b2Dot(rf.sideNormal1, rf.v1); rf.sideOffset2 = b2Dot(rf.sideNormal2, rf.v2); // Clip incident edge against extruded edge1 side edges. b2ClipVertex clipPoints1[2]; b2ClipVertex clipPoints2[2]; int32 np; // Clip to box side 1 np = b2ClipSegmentToLine(clipPoints1, ie, rf.sideNormal1, rf.sideOffset1, rf.i1); if (np < b2_maxManifoldPoints) { return; } // Clip to negative box side 1 np = b2ClipSegmentToLine(clipPoints2, clipPoints1, rf.sideNormal2, rf.sideOffset2, rf.i2); if (np < b2_maxManifoldPoints) { return; } // Now clipPoints2 contains the clipped points. if (primaryAxis.type == b2EPAxis::e_edgeA) { manifold->localNormal = rf.normal; manifold->localPoint = rf.v1; } else { manifold->localNormal = polygonB->m_normals[rf.i1]; manifold->localPoint = polygonB->m_vertices[rf.i1]; } int32 pointCount = 0; for (int32 i = 0; i < b2_maxManifoldPoints; ++i) { float32 separation; separation = b2Dot(rf.normal, clipPoints2[i].v - rf.v1); if (separation <= m_radius) { b2ManifoldPoint* cp = manifold->points + pointCount; if (primaryAxis.type == b2EPAxis::e_edgeA) { cp->localPoint = b2MulT(m_xf, clipPoints2[i].v); cp->id = clipPoints2[i].id; } else { cp->localPoint = clipPoints2[i].v; cp->id.cf.typeA = clipPoints2[i].id.cf.typeB; cp->id.cf.typeB = clipPoints2[i].id.cf.typeA; cp->id.cf.indexA = clipPoints2[i].id.cf.indexB; cp->id.cf.indexB = clipPoints2[i].id.cf.indexA; } ++pointCount; } } manifold->pointCount = pointCount; } b2EPAxis b2EPCollider::ComputeEdgeSeparation() { b2EPAxis axis; axis.type = b2EPAxis::e_edgeA; axis.index = m_front ? 0 : 1; axis.separation = FLT_MAX; for (int32 i = 0; i < m_polygonB.count; ++i) { float32 s = b2Dot(m_normal, m_polygonB.vertices[i] - m_v1); if (s < axis.separation) { axis.separation = s; } } return axis; } b2EPAxis b2EPCollider::ComputePolygonSeparation() { b2EPAxis axis; axis.type = b2EPAxis::e_unknown; axis.index = -1; axis.separation = -FLT_MAX; b2Vec2 perp(-m_normal.y, m_normal.x); for (int32 i = 0; i < m_polygonB.count; ++i) { b2Vec2 n = -m_polygonB.normals[i]; float32 s1 = b2Dot(n, m_polygonB.vertices[i] - m_v1); float32 s2 = b2Dot(n, m_polygonB.vertices[i] - m_v2); float32 s = b2Min(s1, s2); if (s > m_radius) { // No collision axis.type = b2EPAxis::e_edgeB; axis.index = i; axis.separation = s; return axis; } // Adjacency if (b2Dot(n, perp) >= 0.0f) { if (b2Dot(n - m_upperLimit, m_normal) < -b2_angularSlop) { continue; } } else { if (b2Dot(n - m_lowerLimit, m_normal) < -b2_angularSlop) { continue; } } if (s > axis.separation) { axis.type = b2EPAxis::e_edgeB; axis.index = i; axis.separation = s; } } return axis; } void b2CollideEdgeAndPolygon( b2Manifold* manifold, const b2EdgeShape* edgeA, const b2Transform& xfA, const b2PolygonShape* polygonB, const b2Transform& xfB) { b2EPCollider collider; collider.Collide(manifold, edgeA, xfA, polygonB, xfB); } ================================================ FILE: app/src/main/cpp/b2CollidePolygon.cpp ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Collision/b2Collision.h" #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2PolygonShape.h" // Find the max separation between poly1 and poly2 using edge normals from poly1. static float32 b2FindMaxSeparation(int32* edgeIndex, const b2PolygonShape* poly1, const b2Transform& xf1, const b2PolygonShape* poly2, const b2Transform& xf2) { int32 count1 = poly1->m_count; int32 count2 = poly2->m_count; const b2Vec2* n1s = poly1->m_normals; const b2Vec2* v1s = poly1->m_vertices; const b2Vec2* v2s = poly2->m_vertices; b2Transform xf = b2MulT(xf2, xf1); int32 bestIndex = 0; float32 maxSeparation = -b2_maxFloat; for (int32 i = 0; i < count1; ++i) { // Get poly1 normal in frame2. b2Vec2 n = b2Mul(xf.q, n1s[i]); b2Vec2 v1 = b2Mul(xf, v1s[i]); // Find deepest point for normal i. float32 si = b2_maxFloat; for (int32 j = 0; j < count2; ++j) { float32 sij = b2Dot(n, v2s[j] - v1); if (sij < si) { si = sij; } } if (si > maxSeparation) { maxSeparation = si; bestIndex = i; } } *edgeIndex = bestIndex; return maxSeparation; } static void b2FindIncidentEdge(b2ClipVertex c[2], const b2PolygonShape* poly1, const b2Transform& xf1, int32 edge1, const b2PolygonShape* poly2, const b2Transform& xf2) { const b2Vec2* normals1 = poly1->m_normals; int32 count2 = poly2->m_count; const b2Vec2* vertices2 = poly2->m_vertices; const b2Vec2* normals2 = poly2->m_normals; b2Assert(0 <= edge1 && edge1 < poly1->m_count); // Get the normal of the reference edge in poly2's frame. b2Vec2 normal1 = b2MulT(xf2.q, b2Mul(xf1.q, normals1[edge1])); // Find the incident edge on poly2. int32 index = 0; float32 minDot = b2_maxFloat; for (int32 i = 0; i < count2; ++i) { float32 dot = b2Dot(normal1, normals2[i]); if (dot < minDot) { minDot = dot; index = i; } } // Build the clip vertices for the incident edge. int32 i1 = index; int32 i2 = i1 + 1 < count2 ? i1 + 1 : 0; c[0].v = b2Mul(xf2, vertices2[i1]); c[0].id.cf.indexA = (uint8)edge1; c[0].id.cf.indexB = (uint8)i1; c[0].id.cf.typeA = b2ContactFeature::e_face; c[0].id.cf.typeB = b2ContactFeature::e_vertex; c[1].v = b2Mul(xf2, vertices2[i2]); c[1].id.cf.indexA = (uint8)edge1; c[1].id.cf.indexB = (uint8)i2; c[1].id.cf.typeA = b2ContactFeature::e_face; c[1].id.cf.typeB = b2ContactFeature::e_vertex; } // Find edge normal of max separation on A - return if separating axis is found // Find edge normal of max separation on B - return if separation axis is found // Choose reference edge as min(minA, minB) // Find incident edge // Clip // The normal points from 1 to 2 void b2CollidePolygons(b2Manifold* manifold, const b2PolygonShape* polyA, const b2Transform& xfA, const b2PolygonShape* polyB, const b2Transform& xfB) { manifold->pointCount = 0; float32 totalRadius = polyA->m_radius + polyB->m_radius; int32 edgeA = 0; float32 separationA = b2FindMaxSeparation(&edgeA, polyA, xfA, polyB, xfB); if (separationA > totalRadius) return; int32 edgeB = 0; float32 separationB = b2FindMaxSeparation(&edgeB, polyB, xfB, polyA, xfA); if (separationB > totalRadius) return; const b2PolygonShape* poly1; // reference polygon const b2PolygonShape* poly2; // incident polygon b2Transform xf1, xf2; int32 edge1; // reference edge uint8 flip; const float32 k_tol = 0.1f * b2_linearSlop; if (separationB > separationA + k_tol) { poly1 = polyB; poly2 = polyA; xf1 = xfB; xf2 = xfA; edge1 = edgeB; manifold->type = b2Manifold::e_faceB; flip = 1; } else { poly1 = polyA; poly2 = polyB; xf1 = xfA; xf2 = xfB; edge1 = edgeA; manifold->type = b2Manifold::e_faceA; flip = 0; } b2ClipVertex incidentEdge[2]; b2FindIncidentEdge(incidentEdge, poly1, xf1, edge1, poly2, xf2); int32 count1 = poly1->m_count; const b2Vec2* vertices1 = poly1->m_vertices; int32 iv1 = edge1; int32 iv2 = edge1 + 1 < count1 ? edge1 + 1 : 0; b2Vec2 v11 = vertices1[iv1]; b2Vec2 v12 = vertices1[iv2]; b2Vec2 localTangent = v12 - v11; localTangent.Normalize(); b2Vec2 localNormal = b2Cross(localTangent, 1.0f); b2Vec2 planePoint = 0.5f * (v11 + v12); b2Vec2 tangent = b2Mul(xf1.q, localTangent); b2Vec2 normal = b2Cross(tangent, 1.0f); v11 = b2Mul(xf1, v11); v12 = b2Mul(xf1, v12); // Face offset. float32 frontOffset = b2Dot(normal, v11); // Side offsets, extended by polytope skin thickness. float32 sideOffset1 = -b2Dot(tangent, v11) + totalRadius; float32 sideOffset2 = b2Dot(tangent, v12) + totalRadius; // Clip incident edge against extruded edge1 side edges. b2ClipVertex clipPoints1[2]; b2ClipVertex clipPoints2[2]; int np; // Clip to box side 1 np = b2ClipSegmentToLine(clipPoints1, incidentEdge, -tangent, sideOffset1, iv1); if (np < 2) return; // Clip to negative box side 1 np = b2ClipSegmentToLine(clipPoints2, clipPoints1, tangent, sideOffset2, iv2); if (np < 2) { return; } // Now clipPoints2 contains the clipped points. manifold->localNormal = localNormal; manifold->localPoint = planePoint; int32 pointCount = 0; for (int32 i = 0; i < b2_maxManifoldPoints; ++i) { float32 separation = b2Dot(normal, clipPoints2[i].v) - frontOffset; if (separation <= totalRadius) { b2ManifoldPoint* cp = manifold->points + pointCount; cp->localPoint = b2MulT(xf2, clipPoints2[i].v); cp->id = clipPoints2[i].id; if (flip) { // Swap features b2ContactFeature cf = cp->id.cf; cp->id.cf.indexA = cf.indexB; cp->id.cf.indexB = cf.indexA; cp->id.cf.typeA = cf.typeB; cp->id.cf.typeB = cf.typeA; } ++pointCount; } } manifold->pointCount = pointCount; } ================================================ FILE: app/src/main/cpp/b2Collision.cpp ================================================ /* * Copyright (c) 2007-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Collision/b2Collision.h" #include "isEngine/ext_lib/Box2D/Collision/b2Distance.h" void b2WorldManifold::Initialize(const b2Manifold* manifold, const b2Transform& xfA, float32 radiusA, const b2Transform& xfB, float32 radiusB) { if (manifold->pointCount == 0) { return; } switch (manifold->type) { case b2Manifold::e_circles: { normal.Set(1.0f, 0.0f); b2Vec2 pointA = b2Mul(xfA, manifold->localPoint); b2Vec2 pointB = b2Mul(xfB, manifold->points[0].localPoint); if (b2DistanceSquared(pointA, pointB) > b2_epsilon * b2_epsilon) { normal = pointB - pointA; normal.Normalize(); } b2Vec2 cA = pointA + radiusA * normal; b2Vec2 cB = pointB - radiusB * normal; points[0] = 0.5f * (cA + cB); separations[0] = b2Dot(cB - cA, normal); } break; case b2Manifold::e_faceA: { normal = b2Mul(xfA.q, manifold->localNormal); b2Vec2 planePoint = b2Mul(xfA, manifold->localPoint); for (int32 i = 0; i < manifold->pointCount; ++i) { b2Vec2 clipPoint = b2Mul(xfB, manifold->points[i].localPoint); b2Vec2 cA = clipPoint + (radiusA - b2Dot(clipPoint - planePoint, normal)) * normal; b2Vec2 cB = clipPoint - radiusB * normal; points[i] = 0.5f * (cA + cB); separations[i] = b2Dot(cB - cA, normal); } } break; case b2Manifold::e_faceB: { normal = b2Mul(xfB.q, manifold->localNormal); b2Vec2 planePoint = b2Mul(xfB, manifold->localPoint); for (int32 i = 0; i < manifold->pointCount; ++i) { b2Vec2 clipPoint = b2Mul(xfA, manifold->points[i].localPoint); b2Vec2 cB = clipPoint + (radiusB - b2Dot(clipPoint - planePoint, normal)) * normal; b2Vec2 cA = clipPoint - radiusA * normal; points[i] = 0.5f * (cA + cB); separations[i] = b2Dot(cA - cB, normal); } // Ensure normal points from A to B. normal = -normal; } break; } } void b2GetPointStates(b2PointState state1[b2_maxManifoldPoints], b2PointState state2[b2_maxManifoldPoints], const b2Manifold* manifold1, const b2Manifold* manifold2) { for (int32 i = 0; i < b2_maxManifoldPoints; ++i) { state1[i] = b2_nullState; state2[i] = b2_nullState; } // Detect persists and removes. for (int32 i = 0; i < manifold1->pointCount; ++i) { b2ContactID id = manifold1->points[i].id; state1[i] = b2_removeState; for (int32 j = 0; j < manifold2->pointCount; ++j) { if (manifold2->points[j].id.key == id.key) { state1[i] = b2_persistState; break; } } } // Detect persists and adds. for (int32 i = 0; i < manifold2->pointCount; ++i) { b2ContactID id = manifold2->points[i].id; state2[i] = b2_addState; for (int32 j = 0; j < manifold1->pointCount; ++j) { if (manifold1->points[j].id.key == id.key) { state2[i] = b2_persistState; break; } } } } // From Real-time Collision Detection, p179. bool b2AABB::RayCast(b2RayCastOutput* output, const b2RayCastInput& input) const { float32 tmin = -b2_maxFloat; float32 tmax = b2_maxFloat; b2Vec2 p = input.p1; b2Vec2 d = input.p2 - input.p1; b2Vec2 absD = b2Abs(d); b2Vec2 normal; for (int32 i = 0; i < 2; ++i) { if (absD(i) < b2_epsilon) { // Parallel. if (p(i) < lowerBound(i) || upperBound(i) < p(i)) { return false; } } else { float32 inv_d = 1.0f / d(i); float32 t1 = (lowerBound(i) - p(i)) * inv_d; float32 t2 = (upperBound(i) - p(i)) * inv_d; // Sign of the normal vector. float32 s = -1.0f; if (t1 > t2) { b2Swap(t1, t2); s = 1.0f; } // Push the min up if (t1 > tmin) { normal.SetZero(); normal(i) = s; tmin = t1; } // Pull the max down tmax = b2Min(tmax, t2); if (tmin > tmax) { return false; } } } // Does the ray start inside the box? // Does the ray intersect beyond the max fraction? if (tmin < 0.0f || input.maxFraction < tmin) { return false; } // Intersection. output->fraction = tmin; output->normal = normal; return true; } // Sutherland-Hodgman clipping. int32 b2ClipSegmentToLine(b2ClipVertex vOut[2], const b2ClipVertex vIn[2], const b2Vec2& normal, float32 offset, int32 vertexIndexA) { // Start with no output points int32 numOut = 0; // Calculate the distance of end points to the line float32 distance0 = b2Dot(normal, vIn[0].v) - offset; float32 distance1 = b2Dot(normal, vIn[1].v) - offset; // If the points are behind the plane if (distance0 <= 0.0f) vOut[numOut++] = vIn[0]; if (distance1 <= 0.0f) vOut[numOut++] = vIn[1]; // If the points are on different sides of the plane if (distance0 * distance1 < 0.0f) { // Find intersection point of edge and plane float32 interp = distance0 / (distance0 - distance1); vOut[numOut].v = vIn[0].v + interp * (vIn[1].v - vIn[0].v); // VertexA is hitting edgeB. vOut[numOut].id.cf.indexA = static_cast(vertexIndexA); vOut[numOut].id.cf.indexB = vIn[0].id.cf.indexB; vOut[numOut].id.cf.typeA = b2ContactFeature::e_vertex; vOut[numOut].id.cf.typeB = b2ContactFeature::e_face; ++numOut; } return numOut; } bool b2TestOverlap( const b2Shape* shapeA, int32 indexA, const b2Shape* shapeB, int32 indexB, const b2Transform& xfA, const b2Transform& xfB) { b2DistanceInput input; input.proxyA.Set(shapeA, indexA); input.proxyB.Set(shapeB, indexB); input.transformA = xfA; input.transformB = xfB; input.useRadii = true; b2SimplexCache cache; cache.count = 0; b2DistanceOutput output; b2Distance(&output, &cache, &input); return output.distance < 10.0f * b2_epsilon; } ================================================ FILE: app/src/main/cpp/b2Contact.cpp ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Dynamics/Contacts/b2Contact.h" #include "isEngine/ext_lib/Box2D/Dynamics/Contacts/b2CircleContact.h" #include "isEngine/ext_lib/Box2D/Dynamics/Contacts/b2CircleContact.h" #include "isEngine/ext_lib/Box2D/Dynamics/Contacts/b2PolygonContact.h" #include "isEngine/ext_lib/Box2D/Dynamics/Contacts/b2EdgeAndCircleContact.h" #include "isEngine/ext_lib/Box2D/Dynamics/Contacts/b2EdgeAndPolygonContact.h" #include "isEngine/ext_lib/Box2D/Dynamics/Contacts/b2PolygonAndCircleContact.h" #include "isEngine/ext_lib/Box2D/Dynamics/Contacts/b2ChainAndCircleContact.h" #include "isEngine/ext_lib/Box2D/Dynamics/Contacts/b2ChainAndPolygonContact.h" #include "isEngine/ext_lib/Box2D/Dynamics/Contacts/b2ContactSolver.h" #include "isEngine/ext_lib/Box2D/Collision/b2Collision.h" #include "isEngine/ext_lib/Box2D/Collision/b2TimeOfImpact.h" #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2Shape.h" #include "isEngine/ext_lib/Box2D/Common/b2BlockAllocator.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Body.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Fixture.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2World.h" b2ContactRegister b2Contact::s_registers[b2Shape::e_typeCount][b2Shape::e_typeCount]; bool b2Contact::s_initialized = false; void b2Contact::InitializeRegisters() { AddType(b2CircleContact::Create, b2CircleContact::Destroy, b2Shape::e_circle, b2Shape::e_circle); AddType(b2PolygonAndCircleContact::Create, b2PolygonAndCircleContact::Destroy, b2Shape::e_polygon, b2Shape::e_circle); AddType(b2PolygonContact::Create, b2PolygonContact::Destroy, b2Shape::e_polygon, b2Shape::e_polygon); AddType(b2EdgeAndCircleContact::Create, b2EdgeAndCircleContact::Destroy, b2Shape::e_edge, b2Shape::e_circle); AddType(b2EdgeAndPolygonContact::Create, b2EdgeAndPolygonContact::Destroy, b2Shape::e_edge, b2Shape::e_polygon); AddType(b2ChainAndCircleContact::Create, b2ChainAndCircleContact::Destroy, b2Shape::e_chain, b2Shape::e_circle); AddType(b2ChainAndPolygonContact::Create, b2ChainAndPolygonContact::Destroy, b2Shape::e_chain, b2Shape::e_polygon); } void b2Contact::AddType(b2ContactCreateFcn* createFcn, b2ContactDestroyFcn* destoryFcn, b2Shape::Type type1, b2Shape::Type type2) { b2Assert(0 <= type1 && type1 < b2Shape::e_typeCount); b2Assert(0 <= type2 && type2 < b2Shape::e_typeCount); s_registers[type1][type2].createFcn = createFcn; s_registers[type1][type2].destroyFcn = destoryFcn; s_registers[type1][type2].primary = true; if (type1 != type2) { s_registers[type2][type1].createFcn = createFcn; s_registers[type2][type1].destroyFcn = destoryFcn; s_registers[type2][type1].primary = false; } } b2Contact* b2Contact::Create(b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB, b2BlockAllocator* allocator) { if (s_initialized == false) { InitializeRegisters(); s_initialized = true; } b2Shape::Type type1 = fixtureA->GetType(); b2Shape::Type type2 = fixtureB->GetType(); b2Assert(0 <= type1 && type1 < b2Shape::e_typeCount); b2Assert(0 <= type2 && type2 < b2Shape::e_typeCount); b2ContactCreateFcn* createFcn = s_registers[type1][type2].createFcn; if (createFcn) { if (s_registers[type1][type2].primary) { return createFcn(fixtureA, indexA, fixtureB, indexB, allocator); } else { return createFcn(fixtureB, indexB, fixtureA, indexA, allocator); } } else { return NULL; } } void b2Contact::Destroy(b2Contact* contact, b2BlockAllocator* allocator) { b2Assert(s_initialized == true); b2Fixture* fixtureA = contact->m_fixtureA; b2Fixture* fixtureB = contact->m_fixtureB; if (contact->m_manifold.pointCount > 0 && fixtureA->IsSensor() == false && fixtureB->IsSensor() == false) { fixtureA->GetBody()->SetAwake(true); fixtureB->GetBody()->SetAwake(true); } b2Shape::Type typeA = fixtureA->GetType(); b2Shape::Type typeB = fixtureB->GetType(); b2Assert(0 <= typeA && typeB < b2Shape::e_typeCount); b2Assert(0 <= typeA && typeB < b2Shape::e_typeCount); b2ContactDestroyFcn* destroyFcn = s_registers[typeA][typeB].destroyFcn; destroyFcn(contact, allocator); } b2Contact::b2Contact(b2Fixture* fA, int32 indexA, b2Fixture* fB, int32 indexB) { m_flags = e_enabledFlag; m_fixtureA = fA; m_fixtureB = fB; m_indexA = indexA; m_indexB = indexB; m_manifold.pointCount = 0; m_prev = NULL; m_next = NULL; m_nodeA.contact = NULL; m_nodeA.prev = NULL; m_nodeA.next = NULL; m_nodeA.other = NULL; m_nodeB.contact = NULL; m_nodeB.prev = NULL; m_nodeB.next = NULL; m_nodeB.other = NULL; m_toiCount = 0; m_friction = b2MixFriction(m_fixtureA->m_friction, m_fixtureB->m_friction); m_restitution = b2MixRestitution(m_fixtureA->m_restitution, m_fixtureB->m_restitution); m_tangentSpeed = 0.0f; } // Update the contact manifold and touching status. // Note: do not assume the fixture AABBs are overlapping or are valid. void b2Contact::Update(b2ContactListener* listener) { b2Manifold oldManifold = m_manifold; // Re-enable this contact. m_flags |= e_enabledFlag; bool touching = false; bool wasTouching = (m_flags & e_touchingFlag) == e_touchingFlag; bool sensorA = m_fixtureA->IsSensor(); bool sensorB = m_fixtureB->IsSensor(); bool sensor = sensorA || sensorB; b2Body* bodyA = m_fixtureA->GetBody(); b2Body* bodyB = m_fixtureB->GetBody(); const b2Transform& xfA = bodyA->GetTransform(); const b2Transform& xfB = bodyB->GetTransform(); // Is this contact a sensor? if (sensor) { const b2Shape* shapeA = m_fixtureA->GetShape(); const b2Shape* shapeB = m_fixtureB->GetShape(); touching = b2TestOverlap(shapeA, m_indexA, shapeB, m_indexB, xfA, xfB); // Sensors don't generate manifolds. m_manifold.pointCount = 0; } else { Evaluate(&m_manifold, xfA, xfB); touching = m_manifold.pointCount > 0; // Match old contact ids to new contact ids and copy the // stored impulses to warm start the solver. for (int32 i = 0; i < m_manifold.pointCount; ++i) { b2ManifoldPoint* mp2 = m_manifold.points + i; mp2->normalImpulse = 0.0f; mp2->tangentImpulse = 0.0f; b2ContactID id2 = mp2->id; for (int32 j = 0; j < oldManifold.pointCount; ++j) { b2ManifoldPoint* mp1 = oldManifold.points + j; if (mp1->id.key == id2.key) { mp2->normalImpulse = mp1->normalImpulse; mp2->tangentImpulse = mp1->tangentImpulse; break; } } } if (touching != wasTouching) { bodyA->SetAwake(true); bodyB->SetAwake(true); } } if (touching) { m_flags |= e_touchingFlag; } else { m_flags &= ~e_touchingFlag; } if (wasTouching == false && touching == true && listener) { listener->BeginContact(this); } if (wasTouching == true && touching == false && listener) { listener->EndContact(this); } if (sensor == false && touching && listener) { listener->PreSolve(this, &oldManifold); } } ================================================ FILE: app/src/main/cpp/b2ContactManager.cpp ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Dynamics/b2ContactManager.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Body.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Fixture.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2WorldCallbacks.h" #include "isEngine/ext_lib/Box2D/Dynamics/Contacts/b2Contact.h" b2ContactFilter b2_defaultFilter; b2ContactListener b2_defaultListener; b2ContactManager::b2ContactManager() { m_contactList = NULL; m_contactCount = 0; m_contactFilter = &b2_defaultFilter; m_contactListener = &b2_defaultListener; m_allocator = NULL; } void b2ContactManager::Destroy(b2Contact* c) { b2Fixture* fixtureA = c->GetFixtureA(); b2Fixture* fixtureB = c->GetFixtureB(); b2Body* bodyA = fixtureA->GetBody(); b2Body* bodyB = fixtureB->GetBody(); if (m_contactListener && c->IsTouching()) { m_contactListener->EndContact(c); } // Remove from the world. if (c->m_prev) { c->m_prev->m_next = c->m_next; } if (c->m_next) { c->m_next->m_prev = c->m_prev; } if (c == m_contactList) { m_contactList = c->m_next; } // Remove from body 1 if (c->m_nodeA.prev) { c->m_nodeA.prev->next = c->m_nodeA.next; } if (c->m_nodeA.next) { c->m_nodeA.next->prev = c->m_nodeA.prev; } if (&c->m_nodeA == bodyA->m_contactList) { bodyA->m_contactList = c->m_nodeA.next; } // Remove from body 2 if (c->m_nodeB.prev) { c->m_nodeB.prev->next = c->m_nodeB.next; } if (c->m_nodeB.next) { c->m_nodeB.next->prev = c->m_nodeB.prev; } if (&c->m_nodeB == bodyB->m_contactList) { bodyB->m_contactList = c->m_nodeB.next; } // Call the factory. b2Contact::Destroy(c, m_allocator); --m_contactCount; } // This is the top level collision call for the time step. Here // all the narrow phase collision is processed for the world // contact list. void b2ContactManager::Collide() { // Update awake contacts. b2Contact* c = m_contactList; while (c) { b2Fixture* fixtureA = c->GetFixtureA(); b2Fixture* fixtureB = c->GetFixtureB(); int32 indexA = c->GetChildIndexA(); int32 indexB = c->GetChildIndexB(); b2Body* bodyA = fixtureA->GetBody(); b2Body* bodyB = fixtureB->GetBody(); // Is this contact flagged for filtering? if (c->m_flags & b2Contact::e_filterFlag) { // Should these bodies collide? if (bodyB->ShouldCollide(bodyA) == false) { b2Contact* cNuke = c; c = cNuke->GetNext(); Destroy(cNuke); continue; } // Check user filtering. if (m_contactFilter && m_contactFilter->ShouldCollide(fixtureA, fixtureB) == false) { b2Contact* cNuke = c; c = cNuke->GetNext(); Destroy(cNuke); continue; } // Clear the filtering flag. c->m_flags &= ~b2Contact::e_filterFlag; } bool activeA = bodyA->IsAwake() && bodyA->m_type != b2_staticBody; bool activeB = bodyB->IsAwake() && bodyB->m_type != b2_staticBody; // At least one body must be awake and it must be dynamic or kinematic. if (activeA == false && activeB == false) { c = c->GetNext(); continue; } int32 proxyIdA = fixtureA->m_proxies[indexA].proxyId; int32 proxyIdB = fixtureB->m_proxies[indexB].proxyId; bool overlap = m_broadPhase.TestOverlap(proxyIdA, proxyIdB); // Here we destroy contacts that cease to overlap in the broad-phase. if (overlap == false) { b2Contact* cNuke = c; c = cNuke->GetNext(); Destroy(cNuke); continue; } // The contact persists. c->Update(m_contactListener); c = c->GetNext(); } } void b2ContactManager::FindNewContacts() { m_broadPhase.UpdatePairs(this); } void b2ContactManager::AddPair(void* proxyUserDataA, void* proxyUserDataB) { b2FixtureProxy* proxyA = (b2FixtureProxy*)proxyUserDataA; b2FixtureProxy* proxyB = (b2FixtureProxy*)proxyUserDataB; b2Fixture* fixtureA = proxyA->fixture; b2Fixture* fixtureB = proxyB->fixture; int32 indexA = proxyA->childIndex; int32 indexB = proxyB->childIndex; b2Body* bodyA = fixtureA->GetBody(); b2Body* bodyB = fixtureB->GetBody(); // Are the fixtures on the same body? if (bodyA == bodyB) { return; } // TODO_ERIN use a hash table to remove a potential bottleneck when both // bodies have a lot of contacts. // Does a contact already exist? b2ContactEdge* edge = bodyB->GetContactList(); while (edge) { if (edge->other == bodyA) { b2Fixture* fA = edge->contact->GetFixtureA(); b2Fixture* fB = edge->contact->GetFixtureB(); int32 iA = edge->contact->GetChildIndexA(); int32 iB = edge->contact->GetChildIndexB(); if (fA == fixtureA && fB == fixtureB && iA == indexA && iB == indexB) { // A contact already exists. return; } if (fA == fixtureB && fB == fixtureA && iA == indexB && iB == indexA) { // A contact already exists. return; } } edge = edge->next; } // Does a joint override collision? Is at least one body dynamic? if (bodyB->ShouldCollide(bodyA) == false) { return; } // Check user filtering. if (m_contactFilter && m_contactFilter->ShouldCollide(fixtureA, fixtureB) == false) { return; } // Call the factory. b2Contact* c = b2Contact::Create(fixtureA, indexA, fixtureB, indexB, m_allocator); if (c == NULL) { return; } // Contact creation may swap fixtures. fixtureA = c->GetFixtureA(); fixtureB = c->GetFixtureB(); indexA = c->GetChildIndexA(); indexB = c->GetChildIndexB(); bodyA = fixtureA->GetBody(); bodyB = fixtureB->GetBody(); // Insert into the world. c->m_prev = NULL; c->m_next = m_contactList; if (m_contactList != NULL) { m_contactList->m_prev = c; } m_contactList = c; // Connect to island graph. // Connect to body A c->m_nodeA.contact = c; c->m_nodeA.other = bodyB; c->m_nodeA.prev = NULL; c->m_nodeA.next = bodyA->m_contactList; if (bodyA->m_contactList != NULL) { bodyA->m_contactList->prev = &c->m_nodeA; } bodyA->m_contactList = &c->m_nodeA; // Connect to body B c->m_nodeB.contact = c; c->m_nodeB.other = bodyA; c->m_nodeB.prev = NULL; c->m_nodeB.next = bodyB->m_contactList; if (bodyB->m_contactList != NULL) { bodyB->m_contactList->prev = &c->m_nodeB; } bodyB->m_contactList = &c->m_nodeB; // Wake up the bodies if (fixtureA->IsSensor() == false && fixtureB->IsSensor() == false) { bodyA->SetAwake(true); bodyB->SetAwake(true); } ++m_contactCount; } ================================================ FILE: app/src/main/cpp/b2ContactSolver.cpp ================================================ /* * Copyright (c) 2006-2011 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Dynamics/Contacts/b2ContactSolver.h" #include "isEngine/ext_lib/Box2D/Dynamics/Contacts/b2Contact.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Body.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Fixture.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2World.h" #include "isEngine/ext_lib/Box2D/Common/b2StackAllocator.h" #define B2_DEBUG_SOLVER 0 struct b2ContactPositionConstraint { b2Vec2 localPoints[b2_maxManifoldPoints]; b2Vec2 localNormal; b2Vec2 localPoint; int32 indexA; int32 indexB; float32 invMassA, invMassB; b2Vec2 localCenterA, localCenterB; float32 invIA, invIB; b2Manifold::Type type; float32 radiusA, radiusB; int32 pointCount; }; b2ContactSolver::b2ContactSolver(b2ContactSolverDef* def) { m_step = def->step; m_allocator = def->allocator; m_count = def->count; m_positionConstraints = (b2ContactPositionConstraint*)m_allocator->Allocate(m_count * sizeof(b2ContactPositionConstraint)); m_velocityConstraints = (b2ContactVelocityConstraint*)m_allocator->Allocate(m_count * sizeof(b2ContactVelocityConstraint)); m_positions = def->positions; m_velocities = def->velocities; m_contacts = def->contacts; // Initialize position independent portions of the constraints. for (int32 i = 0; i < m_count; ++i) { b2Contact* contact = m_contacts[i]; b2Fixture* fixtureA = contact->m_fixtureA; b2Fixture* fixtureB = contact->m_fixtureB; b2Shape* shapeA = fixtureA->GetShape(); b2Shape* shapeB = fixtureB->GetShape(); float32 radiusA = shapeA->m_radius; float32 radiusB = shapeB->m_radius; b2Body* bodyA = fixtureA->GetBody(); b2Body* bodyB = fixtureB->GetBody(); b2Manifold* manifold = contact->GetManifold(); int32 pointCount = manifold->pointCount; b2Assert(pointCount > 0); b2ContactVelocityConstraint* vc = m_velocityConstraints + i; vc->friction = contact->m_friction; vc->restitution = contact->m_restitution; vc->tangentSpeed = contact->m_tangentSpeed; vc->indexA = bodyA->m_islandIndex; vc->indexB = bodyB->m_islandIndex; vc->invMassA = bodyA->m_invMass; vc->invMassB = bodyB->m_invMass; vc->invIA = bodyA->m_invI; vc->invIB = bodyB->m_invI; vc->contactIndex = i; vc->pointCount = pointCount; vc->K.SetZero(); vc->normalMass.SetZero(); b2ContactPositionConstraint* pc = m_positionConstraints + i; pc->indexA = bodyA->m_islandIndex; pc->indexB = bodyB->m_islandIndex; pc->invMassA = bodyA->m_invMass; pc->invMassB = bodyB->m_invMass; pc->localCenterA = bodyA->m_sweep.localCenter; pc->localCenterB = bodyB->m_sweep.localCenter; pc->invIA = bodyA->m_invI; pc->invIB = bodyB->m_invI; pc->localNormal = manifold->localNormal; pc->localPoint = manifold->localPoint; pc->pointCount = pointCount; pc->radiusA = radiusA; pc->radiusB = radiusB; pc->type = manifold->type; for (int32 j = 0; j < pointCount; ++j) { b2ManifoldPoint* cp = manifold->points + j; b2VelocityConstraintPoint* vcp = vc->points + j; if (m_step.warmStarting) { vcp->normalImpulse = m_step.dtRatio * cp->normalImpulse; vcp->tangentImpulse = m_step.dtRatio * cp->tangentImpulse; } else { vcp->normalImpulse = 0.0f; vcp->tangentImpulse = 0.0f; } vcp->rA.SetZero(); vcp->rB.SetZero(); vcp->normalMass = 0.0f; vcp->tangentMass = 0.0f; vcp->velocityBias = 0.0f; pc->localPoints[j] = cp->localPoint; } } } b2ContactSolver::~b2ContactSolver() { m_allocator->Free(m_velocityConstraints); m_allocator->Free(m_positionConstraints); } // Initialize position dependent portions of the velocity constraints. void b2ContactSolver::InitializeVelocityConstraints() { for (int32 i = 0; i < m_count; ++i) { b2ContactVelocityConstraint* vc = m_velocityConstraints + i; b2ContactPositionConstraint* pc = m_positionConstraints + i; float32 radiusA = pc->radiusA; float32 radiusB = pc->radiusB; b2Manifold* manifold = m_contacts[vc->contactIndex]->GetManifold(); int32 indexA = vc->indexA; int32 indexB = vc->indexB; float32 mA = vc->invMassA; float32 mB = vc->invMassB; float32 iA = vc->invIA; float32 iB = vc->invIB; b2Vec2 localCenterA = pc->localCenterA; b2Vec2 localCenterB = pc->localCenterB; b2Vec2 cA = m_positions[indexA].c; float32 aA = m_positions[indexA].a; b2Vec2 vA = m_velocities[indexA].v; float32 wA = m_velocities[indexA].w; b2Vec2 cB = m_positions[indexB].c; float32 aB = m_positions[indexB].a; b2Vec2 vB = m_velocities[indexB].v; float32 wB = m_velocities[indexB].w; b2Assert(manifold->pointCount > 0); b2Transform xfA, xfB; xfA.q.Set(aA); xfB.q.Set(aB); xfA.p = cA - b2Mul(xfA.q, localCenterA); xfB.p = cB - b2Mul(xfB.q, localCenterB); b2WorldManifold worldManifold; worldManifold.Initialize(manifold, xfA, radiusA, xfB, radiusB); vc->normal = worldManifold.normal; int32 pointCount = vc->pointCount; for (int32 j = 0; j < pointCount; ++j) { b2VelocityConstraintPoint* vcp = vc->points + j; vcp->rA = worldManifold.points[j] - cA; vcp->rB = worldManifold.points[j] - cB; float32 rnA = b2Cross(vcp->rA, vc->normal); float32 rnB = b2Cross(vcp->rB, vc->normal); float32 kNormal = mA + mB + iA * rnA * rnA + iB * rnB * rnB; vcp->normalMass = kNormal > 0.0f ? 1.0f / kNormal : 0.0f; b2Vec2 tangent = b2Cross(vc->normal, 1.0f); float32 rtA = b2Cross(vcp->rA, tangent); float32 rtB = b2Cross(vcp->rB, tangent); float32 kTangent = mA + mB + iA * rtA * rtA + iB * rtB * rtB; vcp->tangentMass = kTangent > 0.0f ? 1.0f / kTangent : 0.0f; // Setup a velocity bias for restitution. vcp->velocityBias = 0.0f; float32 vRel = b2Dot(vc->normal, vB + b2Cross(wB, vcp->rB) - vA - b2Cross(wA, vcp->rA)); if (vRel < -b2_velocityThreshold) { vcp->velocityBias = -vc->restitution * vRel; } } // If we have two points, then prepare the block solver. if (vc->pointCount == 2) { b2VelocityConstraintPoint* vcp1 = vc->points + 0; b2VelocityConstraintPoint* vcp2 = vc->points + 1; float32 rn1A = b2Cross(vcp1->rA, vc->normal); float32 rn1B = b2Cross(vcp1->rB, vc->normal); float32 rn2A = b2Cross(vcp2->rA, vc->normal); float32 rn2B = b2Cross(vcp2->rB, vc->normal); float32 k11 = mA + mB + iA * rn1A * rn1A + iB * rn1B * rn1B; float32 k22 = mA + mB + iA * rn2A * rn2A + iB * rn2B * rn2B; float32 k12 = mA + mB + iA * rn1A * rn2A + iB * rn1B * rn2B; // Ensure a reasonable condition number. const float32 k_maxConditionNumber = 1000.0f; if (k11 * k11 < k_maxConditionNumber * (k11 * k22 - k12 * k12)) { // K is safe to invert. vc->K.ex.Set(k11, k12); vc->K.ey.Set(k12, k22); vc->normalMass = vc->K.GetInverse(); } else { // The constraints are redundant, just use one. // TODO_ERIN use deepest? vc->pointCount = 1; } } } } void b2ContactSolver::WarmStart() { // Warm start. for (int32 i = 0; i < m_count; ++i) { b2ContactVelocityConstraint* vc = m_velocityConstraints + i; int32 indexA = vc->indexA; int32 indexB = vc->indexB; float32 mA = vc->invMassA; float32 iA = vc->invIA; float32 mB = vc->invMassB; float32 iB = vc->invIB; int32 pointCount = vc->pointCount; b2Vec2 vA = m_velocities[indexA].v; float32 wA = m_velocities[indexA].w; b2Vec2 vB = m_velocities[indexB].v; float32 wB = m_velocities[indexB].w; b2Vec2 normal = vc->normal; b2Vec2 tangent = b2Cross(normal, 1.0f); for (int32 j = 0; j < pointCount; ++j) { b2VelocityConstraintPoint* vcp = vc->points + j; b2Vec2 P = vcp->normalImpulse * normal + vcp->tangentImpulse * tangent; wA -= iA * b2Cross(vcp->rA, P); vA -= mA * P; wB += iB * b2Cross(vcp->rB, P); vB += mB * P; } m_velocities[indexA].v = vA; m_velocities[indexA].w = wA; m_velocities[indexB].v = vB; m_velocities[indexB].w = wB; } } void b2ContactSolver::SolveVelocityConstraints() { for (int32 i = 0; i < m_count; ++i) { b2ContactVelocityConstraint* vc = m_velocityConstraints + i; int32 indexA = vc->indexA; int32 indexB = vc->indexB; float32 mA = vc->invMassA; float32 iA = vc->invIA; float32 mB = vc->invMassB; float32 iB = vc->invIB; int32 pointCount = vc->pointCount; b2Vec2 vA = m_velocities[indexA].v; float32 wA = m_velocities[indexA].w; b2Vec2 vB = m_velocities[indexB].v; float32 wB = m_velocities[indexB].w; b2Vec2 normal = vc->normal; b2Vec2 tangent = b2Cross(normal, 1.0f); float32 friction = vc->friction; b2Assert(pointCount == 1 || pointCount == 2); // Solve tangent constraints first because non-penetration is more important // than friction. for (int32 j = 0; j < pointCount; ++j) { b2VelocityConstraintPoint* vcp = vc->points + j; // Relative velocity at contact b2Vec2 dv = vB + b2Cross(wB, vcp->rB) - vA - b2Cross(wA, vcp->rA); // Compute tangent force float32 vt = b2Dot(dv, tangent) - vc->tangentSpeed; float32 lambda = vcp->tangentMass * (-vt); // b2Clamp the accumulated force float32 maxFriction = friction * vcp->normalImpulse; float32 newImpulse = b2Clamp(vcp->tangentImpulse + lambda, -maxFriction, maxFriction); lambda = newImpulse - vcp->tangentImpulse; vcp->tangentImpulse = newImpulse; // Apply contact impulse b2Vec2 P = lambda * tangent; vA -= mA * P; wA -= iA * b2Cross(vcp->rA, P); vB += mB * P; wB += iB * b2Cross(vcp->rB, P); } // Solve normal constraints if (vc->pointCount == 1) { b2VelocityConstraintPoint* vcp = vc->points + 0; // Relative velocity at contact b2Vec2 dv = vB + b2Cross(wB, vcp->rB) - vA - b2Cross(wA, vcp->rA); // Compute normal impulse float32 vn = b2Dot(dv, normal); float32 lambda = -vcp->normalMass * (vn - vcp->velocityBias); // b2Clamp the accumulated impulse float32 newImpulse = b2Max(vcp->normalImpulse + lambda, 0.0f); lambda = newImpulse - vcp->normalImpulse; vcp->normalImpulse = newImpulse; // Apply contact impulse b2Vec2 P = lambda * normal; vA -= mA * P; wA -= iA * b2Cross(vcp->rA, P); vB += mB * P; wB += iB * b2Cross(vcp->rB, P); } else { // Block solver developed in collaboration with Dirk Gregorius (back in 01/07 on Box2D_Lite). // Build the mini LCP for this contact patch // // vn = A * x + b, vn >= 0, , vn >= 0, x >= 0 and vn_i * x_i = 0 with i = 1..2 // // A = J * W * JT and J = ( -n, -r1 x n, n, r2 x n ) // b = vn0 - velocityBias // // The system is solved using the "Total enumeration method" (s. Murty). The complementary constraint vn_i * x_i // implies that we must have in any solution either vn_i = 0 or x_i = 0. So for the 2D contact problem the cases // vn1 = 0 and vn2 = 0, x1 = 0 and x2 = 0, x1 = 0 and vn2 = 0, x2 = 0 and vn1 = 0 need to be tested. The first valid // solution that satisfies the problem is chosen. // // In order to account of the accumulated impulse 'a' (because of the iterative nature of the solver which only requires // that the accumulated impulse is clamped and not the incremental impulse) we change the impulse variable (x_i). // // Substitute: // // x = a + d // // a := old total impulse // x := new total impulse // d := incremental impulse // // For the current iteration we extend the formula for the incremental impulse // to compute the new total impulse: // // vn = A * d + b // = A * (x - a) + b // = A * x + b - A * a // = A * x + b' // b' = b - A * a; b2VelocityConstraintPoint* cp1 = vc->points + 0; b2VelocityConstraintPoint* cp2 = vc->points + 1; b2Vec2 a(cp1->normalImpulse, cp2->normalImpulse); b2Assert(a.x >= 0.0f && a.y >= 0.0f); // Relative velocity at contact b2Vec2 dv1 = vB + b2Cross(wB, cp1->rB) - vA - b2Cross(wA, cp1->rA); b2Vec2 dv2 = vB + b2Cross(wB, cp2->rB) - vA - b2Cross(wA, cp2->rA); // Compute normal velocity float32 vn1 = b2Dot(dv1, normal); float32 vn2 = b2Dot(dv2, normal); b2Vec2 b; b.x = vn1 - cp1->velocityBias; b.y = vn2 - cp2->velocityBias; // Compute b' b -= b2Mul(vc->K, a); const float32 k_errorTol = 1e-3f; B2_NOT_USED(k_errorTol); for (;;) { // // Case 1: vn = 0 // // 0 = A * x + b' // // Solve for x: // // x = - inv(A) * b' // b2Vec2 x = - b2Mul(vc->normalMass, b); if (x.x >= 0.0f && x.y >= 0.0f) { // Get the incremental impulse b2Vec2 d = x - a; // Apply incremental impulse b2Vec2 P1 = d.x * normal; b2Vec2 P2 = d.y * normal; vA -= mA * (P1 + P2); wA -= iA * (b2Cross(cp1->rA, P1) + b2Cross(cp2->rA, P2)); vB += mB * (P1 + P2); wB += iB * (b2Cross(cp1->rB, P1) + b2Cross(cp2->rB, P2)); // Accumulate cp1->normalImpulse = x.x; cp2->normalImpulse = x.y; #if B2_DEBUG_SOLVER == 1 // Postconditions dv1 = vB + b2Cross(wB, cp1->rB) - vA - b2Cross(wA, cp1->rA); dv2 = vB + b2Cross(wB, cp2->rB) - vA - b2Cross(wA, cp2->rA); // Compute normal velocity vn1 = b2Dot(dv1, normal); vn2 = b2Dot(dv2, normal); b2Assert(b2Abs(vn1 - cp1->velocityBias) < k_errorTol); b2Assert(b2Abs(vn2 - cp2->velocityBias) < k_errorTol); #endif break; } // // Case 2: vn1 = 0 and x2 = 0 // // 0 = a11 * x1 + a12 * 0 + b1' // vn2 = a21 * x1 + a22 * 0 + b2' // x.x = - cp1->normalMass * b.x; x.y = 0.0f; vn1 = 0.0f; vn2 = vc->K.ex.y * x.x + b.y; if (x.x >= 0.0f && vn2 >= 0.0f) { // Get the incremental impulse b2Vec2 d = x - a; // Apply incremental impulse b2Vec2 P1 = d.x * normal; b2Vec2 P2 = d.y * normal; vA -= mA * (P1 + P2); wA -= iA * (b2Cross(cp1->rA, P1) + b2Cross(cp2->rA, P2)); vB += mB * (P1 + P2); wB += iB * (b2Cross(cp1->rB, P1) + b2Cross(cp2->rB, P2)); // Accumulate cp1->normalImpulse = x.x; cp2->normalImpulse = x.y; #if B2_DEBUG_SOLVER == 1 // Postconditions dv1 = vB + b2Cross(wB, cp1->rB) - vA - b2Cross(wA, cp1->rA); // Compute normal velocity vn1 = b2Dot(dv1, normal); b2Assert(b2Abs(vn1 - cp1->velocityBias) < k_errorTol); #endif break; } // // Case 3: vn2 = 0 and x1 = 0 // // vn1 = a11 * 0 + a12 * x2 + b1' // 0 = a21 * 0 + a22 * x2 + b2' // x.x = 0.0f; x.y = - cp2->normalMass * b.y; vn1 = vc->K.ey.x * x.y + b.x; vn2 = 0.0f; if (x.y >= 0.0f && vn1 >= 0.0f) { // Resubstitute for the incremental impulse b2Vec2 d = x - a; // Apply incremental impulse b2Vec2 P1 = d.x * normal; b2Vec2 P2 = d.y * normal; vA -= mA * (P1 + P2); wA -= iA * (b2Cross(cp1->rA, P1) + b2Cross(cp2->rA, P2)); vB += mB * (P1 + P2); wB += iB * (b2Cross(cp1->rB, P1) + b2Cross(cp2->rB, P2)); // Accumulate cp1->normalImpulse = x.x; cp2->normalImpulse = x.y; #if B2_DEBUG_SOLVER == 1 // Postconditions dv2 = vB + b2Cross(wB, cp2->rB) - vA - b2Cross(wA, cp2->rA); // Compute normal velocity vn2 = b2Dot(dv2, normal); b2Assert(b2Abs(vn2 - cp2->velocityBias) < k_errorTol); #endif break; } // // Case 4: x1 = 0 and x2 = 0 // // vn1 = b1 // vn2 = b2; x.x = 0.0f; x.y = 0.0f; vn1 = b.x; vn2 = b.y; if (vn1 >= 0.0f && vn2 >= 0.0f ) { // Resubstitute for the incremental impulse b2Vec2 d = x - a; // Apply incremental impulse b2Vec2 P1 = d.x * normal; b2Vec2 P2 = d.y * normal; vA -= mA * (P1 + P2); wA -= iA * (b2Cross(cp1->rA, P1) + b2Cross(cp2->rA, P2)); vB += mB * (P1 + P2); wB += iB * (b2Cross(cp1->rB, P1) + b2Cross(cp2->rB, P2)); // Accumulate cp1->normalImpulse = x.x; cp2->normalImpulse = x.y; break; } // No solution, give up. This is hit sometimes, but it doesn't seem to matter. break; } } m_velocities[indexA].v = vA; m_velocities[indexA].w = wA; m_velocities[indexB].v = vB; m_velocities[indexB].w = wB; } } void b2ContactSolver::StoreImpulses() { for (int32 i = 0; i < m_count; ++i) { b2ContactVelocityConstraint* vc = m_velocityConstraints + i; b2Manifold* manifold = m_contacts[vc->contactIndex]->GetManifold(); for (int32 j = 0; j < vc->pointCount; ++j) { manifold->points[j].normalImpulse = vc->points[j].normalImpulse; manifold->points[j].tangentImpulse = vc->points[j].tangentImpulse; } } } struct b2PositionSolverManifold { void Initialize(b2ContactPositionConstraint* pc, const b2Transform& xfA, const b2Transform& xfB, int32 index) { b2Assert(pc->pointCount > 0); switch (pc->type) { case b2Manifold::e_circles: { b2Vec2 pointA = b2Mul(xfA, pc->localPoint); b2Vec2 pointB = b2Mul(xfB, pc->localPoints[0]); normal = pointB - pointA; normal.Normalize(); point = 0.5f * (pointA + pointB); separation = b2Dot(pointB - pointA, normal) - pc->radiusA - pc->radiusB; } break; case b2Manifold::e_faceA: { normal = b2Mul(xfA.q, pc->localNormal); b2Vec2 planePoint = b2Mul(xfA, pc->localPoint); b2Vec2 clipPoint = b2Mul(xfB, pc->localPoints[index]); separation = b2Dot(clipPoint - planePoint, normal) - pc->radiusA - pc->radiusB; point = clipPoint; } break; case b2Manifold::e_faceB: { normal = b2Mul(xfB.q, pc->localNormal); b2Vec2 planePoint = b2Mul(xfB, pc->localPoint); b2Vec2 clipPoint = b2Mul(xfA, pc->localPoints[index]); separation = b2Dot(clipPoint - planePoint, normal) - pc->radiusA - pc->radiusB; point = clipPoint; // Ensure normal points from A to B normal = -normal; } break; } } b2Vec2 normal; b2Vec2 point; float32 separation; }; // Sequential solver. bool b2ContactSolver::SolvePositionConstraints() { float32 minSeparation = 0.0f; for (int32 i = 0; i < m_count; ++i) { b2ContactPositionConstraint* pc = m_positionConstraints + i; int32 indexA = pc->indexA; int32 indexB = pc->indexB; b2Vec2 localCenterA = pc->localCenterA; float32 mA = pc->invMassA; float32 iA = pc->invIA; b2Vec2 localCenterB = pc->localCenterB; float32 mB = pc->invMassB; float32 iB = pc->invIB; int32 pointCount = pc->pointCount; b2Vec2 cA = m_positions[indexA].c; float32 aA = m_positions[indexA].a; b2Vec2 cB = m_positions[indexB].c; float32 aB = m_positions[indexB].a; // Solve normal constraints for (int32 j = 0; j < pointCount; ++j) { b2Transform xfA, xfB; xfA.q.Set(aA); xfB.q.Set(aB); xfA.p = cA - b2Mul(xfA.q, localCenterA); xfB.p = cB - b2Mul(xfB.q, localCenterB); b2PositionSolverManifold psm; psm.Initialize(pc, xfA, xfB, j); b2Vec2 normal = psm.normal; b2Vec2 point = psm.point; float32 separation = psm.separation; b2Vec2 rA = point - cA; b2Vec2 rB = point - cB; // Track max constraint error. minSeparation = b2Min(minSeparation, separation); // Prevent large corrections and allow slop. float32 C = b2Clamp(b2_baumgarte * (separation + b2_linearSlop), -b2_maxLinearCorrection, 0.0f); // Compute the effective mass. float32 rnA = b2Cross(rA, normal); float32 rnB = b2Cross(rB, normal); float32 K = mA + mB + iA * rnA * rnA + iB * rnB * rnB; // Compute normal impulse float32 impulse = K > 0.0f ? - C / K : 0.0f; b2Vec2 P = impulse * normal; cA -= mA * P; aA -= iA * b2Cross(rA, P); cB += mB * P; aB += iB * b2Cross(rB, P); } m_positions[indexA].c = cA; m_positions[indexA].a = aA; m_positions[indexB].c = cB; m_positions[indexB].a = aB; } // We can't expect minSpeparation >= -b2_linearSlop because we don't // push the separation above -b2_linearSlop. return minSeparation >= -3.0f * b2_linearSlop; } // Sequential position solver for position constraints. bool b2ContactSolver::SolveTOIPositionConstraints(int32 toiIndexA, int32 toiIndexB) { float32 minSeparation = 0.0f; for (int32 i = 0; i < m_count; ++i) { b2ContactPositionConstraint* pc = m_positionConstraints + i; int32 indexA = pc->indexA; int32 indexB = pc->indexB; b2Vec2 localCenterA = pc->localCenterA; b2Vec2 localCenterB = pc->localCenterB; int32 pointCount = pc->pointCount; float32 mA = 0.0f; float32 iA = 0.0f; if (indexA == toiIndexA || indexA == toiIndexB) { mA = pc->invMassA; iA = pc->invIA; } float32 mB = 0.0f; float32 iB = 0.; if (indexB == toiIndexA || indexB == toiIndexB) { mB = pc->invMassB; iB = pc->invIB; } b2Vec2 cA = m_positions[indexA].c; float32 aA = m_positions[indexA].a; b2Vec2 cB = m_positions[indexB].c; float32 aB = m_positions[indexB].a; // Solve normal constraints for (int32 j = 0; j < pointCount; ++j) { b2Transform xfA, xfB; xfA.q.Set(aA); xfB.q.Set(aB); xfA.p = cA - b2Mul(xfA.q, localCenterA); xfB.p = cB - b2Mul(xfB.q, localCenterB); b2PositionSolverManifold psm; psm.Initialize(pc, xfA, xfB, j); b2Vec2 normal = psm.normal; b2Vec2 point = psm.point; float32 separation = psm.separation; b2Vec2 rA = point - cA; b2Vec2 rB = point - cB; // Track max constraint error. minSeparation = b2Min(minSeparation, separation); // Prevent large corrections and allow slop. float32 C = b2Clamp(b2_toiBaugarte * (separation + b2_linearSlop), -b2_maxLinearCorrection, 0.0f); // Compute the effective mass. float32 rnA = b2Cross(rA, normal); float32 rnB = b2Cross(rB, normal); float32 K = mA + mB + iA * rnA * rnA + iB * rnB * rnB; // Compute normal impulse float32 impulse = K > 0.0f ? - C / K : 0.0f; b2Vec2 P = impulse * normal; cA -= mA * P; aA -= iA * b2Cross(rA, P); cB += mB * P; aB += iB * b2Cross(rB, P); } m_positions[indexA].c = cA; m_positions[indexA].a = aA; m_positions[indexB].c = cB; m_positions[indexB].a = aB; } // We can't expect minSpeparation >= -b2_linearSlop because we don't // push the separation above -b2_linearSlop. return minSeparation >= -1.5f * b2_linearSlop; } ================================================ FILE: app/src/main/cpp/b2Distance.cpp ================================================ /* * Copyright (c) 2007-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Collision/b2Distance.h" #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2CircleShape.h" #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2EdgeShape.h" #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2ChainShape.h" #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2PolygonShape.h" // GJK using Voronoi regions (Christer Ericson) and Barycentric coordinates. int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters; void b2DistanceProxy::Set(const b2Shape* shape, int32 index) { switch (shape->GetType()) { case b2Shape::e_circle: { const b2CircleShape* circle = static_cast(shape); m_vertices = &circle->m_p; m_count = 1; m_radius = circle->m_radius; } break; case b2Shape::e_polygon: { const b2PolygonShape* polygon = static_cast(shape); m_vertices = polygon->m_vertices; m_count = polygon->m_count; m_radius = polygon->m_radius; } break; case b2Shape::e_chain: { const b2ChainShape* chain = static_cast(shape); b2Assert(0 <= index && index < chain->m_count); m_buffer[0] = chain->m_vertices[index]; if (index + 1 < chain->m_count) { m_buffer[1] = chain->m_vertices[index + 1]; } else { m_buffer[1] = chain->m_vertices[0]; } m_vertices = m_buffer; m_count = 2; m_radius = chain->m_radius; } break; case b2Shape::e_edge: { const b2EdgeShape* edge = static_cast(shape); m_vertices = &edge->m_vertex1; m_count = 2; m_radius = edge->m_radius; } break; default: b2Assert(false); } } struct b2SimplexVertex { b2Vec2 wA; // support point in proxyA b2Vec2 wB; // support point in proxyB b2Vec2 w; // wB - wA float32 a; // barycentric coordinate for closest point int32 indexA; // wA index int32 indexB; // wB index }; struct b2Simplex { void ReadCache( const b2SimplexCache* cache, const b2DistanceProxy* proxyA, const b2Transform& transformA, const b2DistanceProxy* proxyB, const b2Transform& transformB) { b2Assert(cache->count <= 3); // Copy data from cache. m_count = cache->count; b2SimplexVertex* vertices = &m_v1; for (int32 i = 0; i < m_count; ++i) { b2SimplexVertex* v = vertices + i; v->indexA = cache->indexA[i]; v->indexB = cache->indexB[i]; b2Vec2 wALocal = proxyA->GetVertex(v->indexA); b2Vec2 wBLocal = proxyB->GetVertex(v->indexB); v->wA = b2Mul(transformA, wALocal); v->wB = b2Mul(transformB, wBLocal); v->w = v->wB - v->wA; v->a = 0.0f; } // Compute the new simplex metric, if it is substantially different than // old metric then flush the simplex. if (m_count > 1) { float32 metric1 = cache->metric; float32 metric2 = GetMetric(); if (metric2 < 0.5f * metric1 || 2.0f * metric1 < metric2 || metric2 < b2_epsilon) { // Reset the simplex. m_count = 0; } } // If the cache is empty or invalid ... if (m_count == 0) { b2SimplexVertex* v = vertices + 0; v->indexA = 0; v->indexB = 0; b2Vec2 wALocal = proxyA->GetVertex(0); b2Vec2 wBLocal = proxyB->GetVertex(0); v->wA = b2Mul(transformA, wALocal); v->wB = b2Mul(transformB, wBLocal); v->w = v->wB - v->wA; v->a = 1.0f; m_count = 1; } } void WriteCache(b2SimplexCache* cache) const { cache->metric = GetMetric(); cache->count = uint16(m_count); const b2SimplexVertex* vertices = &m_v1; for (int32 i = 0; i < m_count; ++i) { cache->indexA[i] = uint8(vertices[i].indexA); cache->indexB[i] = uint8(vertices[i].indexB); } } b2Vec2 GetSearchDirection() const { switch (m_count) { case 1: return -m_v1.w; case 2: { b2Vec2 e12 = m_v2.w - m_v1.w; float32 sgn = b2Cross(e12, -m_v1.w); if (sgn > 0.0f) { // Origin is left of e12. return b2Cross(1.0f, e12); } else { // Origin is right of e12. return b2Cross(e12, 1.0f); } } default: b2Assert(false); return b2Vec2_zero; } } b2Vec2 GetClosestPoint() const { switch (m_count) { case 0: b2Assert(false); return b2Vec2_zero; case 1: return m_v1.w; case 2: return m_v1.a * m_v1.w + m_v2.a * m_v2.w; case 3: return b2Vec2_zero; default: b2Assert(false); return b2Vec2_zero; } } void GetWitnessPoints(b2Vec2* pA, b2Vec2* pB) const { switch (m_count) { case 0: b2Assert(false); break; case 1: *pA = m_v1.wA; *pB = m_v1.wB; break; case 2: *pA = m_v1.a * m_v1.wA + m_v2.a * m_v2.wA; *pB = m_v1.a * m_v1.wB + m_v2.a * m_v2.wB; break; case 3: *pA = m_v1.a * m_v1.wA + m_v2.a * m_v2.wA + m_v3.a * m_v3.wA; *pB = *pA; break; default: b2Assert(false); break; } } float32 GetMetric() const { switch (m_count) { case 0: b2Assert(false); return 0.0f; case 1: return 0.0f; case 2: return b2Distance(m_v1.w, m_v2.w); case 3: return b2Cross(m_v2.w - m_v1.w, m_v3.w - m_v1.w); default: b2Assert(false); return 0.0f; } } void Solve2(); void Solve3(); b2SimplexVertex m_v1, m_v2, m_v3; int32 m_count; }; // Solve a line segment using barycentric coordinates. // // p = a1 * w1 + a2 * w2 // a1 + a2 = 1 // // The vector from the origin to the closest point on the line is // perpendicular to the line. // e12 = w2 - w1 // dot(p, e) = 0 // a1 * dot(w1, e) + a2 * dot(w2, e) = 0 // // 2-by-2 linear system // [1 1 ][a1] = [1] // [w1.e12 w2.e12][a2] = [0] // // Define // d12_1 = dot(w2, e12) // d12_2 = -dot(w1, e12) // d12 = d12_1 + d12_2 // // Solution // a1 = d12_1 / d12 // a2 = d12_2 / d12 void b2Simplex::Solve2() { b2Vec2 w1 = m_v1.w; b2Vec2 w2 = m_v2.w; b2Vec2 e12 = w2 - w1; // w1 region float32 d12_2 = -b2Dot(w1, e12); if (d12_2 <= 0.0f) { // a2 <= 0, so we clamp it to 0 m_v1.a = 1.0f; m_count = 1; return; } // w2 region float32 d12_1 = b2Dot(w2, e12); if (d12_1 <= 0.0f) { // a1 <= 0, so we clamp it to 0 m_v2.a = 1.0f; m_count = 1; m_v1 = m_v2; return; } // Must be in e12 region. float32 inv_d12 = 1.0f / (d12_1 + d12_2); m_v1.a = d12_1 * inv_d12; m_v2.a = d12_2 * inv_d12; m_count = 2; } // Possible regions: // - points[2] // - edge points[0]-points[2] // - edge points[1]-points[2] // - inside the triangle void b2Simplex::Solve3() { b2Vec2 w1 = m_v1.w; b2Vec2 w2 = m_v2.w; b2Vec2 w3 = m_v3.w; // Edge12 // [1 1 ][a1] = [1] // [w1.e12 w2.e12][a2] = [0] // a3 = 0 b2Vec2 e12 = w2 - w1; float32 w1e12 = b2Dot(w1, e12); float32 w2e12 = b2Dot(w2, e12); float32 d12_1 = w2e12; float32 d12_2 = -w1e12; // Edge13 // [1 1 ][a1] = [1] // [w1.e13 w3.e13][a3] = [0] // a2 = 0 b2Vec2 e13 = w3 - w1; float32 w1e13 = b2Dot(w1, e13); float32 w3e13 = b2Dot(w3, e13); float32 d13_1 = w3e13; float32 d13_2 = -w1e13; // Edge23 // [1 1 ][a2] = [1] // [w2.e23 w3.e23][a3] = [0] // a1 = 0 b2Vec2 e23 = w3 - w2; float32 w2e23 = b2Dot(w2, e23); float32 w3e23 = b2Dot(w3, e23); float32 d23_1 = w3e23; float32 d23_2 = -w2e23; // Triangle123 float32 n123 = b2Cross(e12, e13); float32 d123_1 = n123 * b2Cross(w2, w3); float32 d123_2 = n123 * b2Cross(w3, w1); float32 d123_3 = n123 * b2Cross(w1, w2); // w1 region if (d12_2 <= 0.0f && d13_2 <= 0.0f) { m_v1.a = 1.0f; m_count = 1; return; } // e12 if (d12_1 > 0.0f && d12_2 > 0.0f && d123_3 <= 0.0f) { float32 inv_d12 = 1.0f / (d12_1 + d12_2); m_v1.a = d12_1 * inv_d12; m_v2.a = d12_2 * inv_d12; m_count = 2; return; } // e13 if (d13_1 > 0.0f && d13_2 > 0.0f && d123_2 <= 0.0f) { float32 inv_d13 = 1.0f / (d13_1 + d13_2); m_v1.a = d13_1 * inv_d13; m_v3.a = d13_2 * inv_d13; m_count = 2; m_v2 = m_v3; return; } // w2 region if (d12_1 <= 0.0f && d23_2 <= 0.0f) { m_v2.a = 1.0f; m_count = 1; m_v1 = m_v2; return; } // w3 region if (d13_1 <= 0.0f && d23_1 <= 0.0f) { m_v3.a = 1.0f; m_count = 1; m_v1 = m_v3; return; } // e23 if (d23_1 > 0.0f && d23_2 > 0.0f && d123_1 <= 0.0f) { float32 inv_d23 = 1.0f / (d23_1 + d23_2); m_v2.a = d23_1 * inv_d23; m_v3.a = d23_2 * inv_d23; m_count = 2; m_v1 = m_v3; return; } // Must be in triangle123 float32 inv_d123 = 1.0f / (d123_1 + d123_2 + d123_3); m_v1.a = d123_1 * inv_d123; m_v2.a = d123_2 * inv_d123; m_v3.a = d123_3 * inv_d123; m_count = 3; } void b2Distance(b2DistanceOutput* output, b2SimplexCache* cache, const b2DistanceInput* input) { ++b2_gjkCalls; const b2DistanceProxy* proxyA = &input->proxyA; const b2DistanceProxy* proxyB = &input->proxyB; b2Transform transformA = input->transformA; b2Transform transformB = input->transformB; // Initialize the simplex. b2Simplex simplex; simplex.ReadCache(cache, proxyA, transformA, proxyB, transformB); // Get simplex vertices as an array. b2SimplexVertex* vertices = &simplex.m_v1; const int32 k_maxIters = 20; // These store the vertices of the last simplex so that we // can check for duplicates and prevent cycling. int32 saveA[3], saveB[3]; int32 saveCount = 0; float32 distanceSqr1 = b2_maxFloat; float32 distanceSqr2 = distanceSqr1; // Main iteration loop. int32 iter = 0; while (iter < k_maxIters) { // Copy simplex so we can identify duplicates. saveCount = simplex.m_count; for (int32 i = 0; i < saveCount; ++i) { saveA[i] = vertices[i].indexA; saveB[i] = vertices[i].indexB; } switch (simplex.m_count) { case 1: break; case 2: simplex.Solve2(); break; case 3: simplex.Solve3(); break; default: b2Assert(false); } // If we have 3 points, then the origin is in the corresponding triangle. if (simplex.m_count == 3) { break; } // Compute closest point. b2Vec2 p = simplex.GetClosestPoint(); distanceSqr2 = p.LengthSquared(); // Ensure progress if (distanceSqr2 >= distanceSqr1) { //break; } distanceSqr1 = distanceSqr2; // Get search direction. b2Vec2 d = simplex.GetSearchDirection(); // Ensure the search direction is numerically fit. if (d.LengthSquared() < b2_epsilon * b2_epsilon) { // The origin is probably contained by a line segment // or triangle. Thus the shapes are overlapped. // We can't return zero here even though there may be overlap. // In case the simplex is a point, segment, or triangle it is difficult // to determine if the origin is contained in the CSO or very close to it. break; } // Compute a tentative new simplex vertex using support points. b2SimplexVertex* vertex = vertices + simplex.m_count; vertex->indexA = proxyA->GetSupport(b2MulT(transformA.q, -d)); vertex->wA = b2Mul(transformA, proxyA->GetVertex(vertex->indexA)); b2Vec2 wBLocal; vertex->indexB = proxyB->GetSupport(b2MulT(transformB.q, d)); vertex->wB = b2Mul(transformB, proxyB->GetVertex(vertex->indexB)); vertex->w = vertex->wB - vertex->wA; // Iteration count is equated to the number of support point calls. ++iter; ++b2_gjkIters; // Check for duplicate support points. This is the main termination criteria. bool duplicate = false; for (int32 i = 0; i < saveCount; ++i) { if (vertex->indexA == saveA[i] && vertex->indexB == saveB[i]) { duplicate = true; break; } } // If we found a duplicate support point we must exit to avoid cycling. if (duplicate) { break; } // New vertex is ok and needed. ++simplex.m_count; } b2_gjkMaxIters = b2Max(b2_gjkMaxIters, iter); // Prepare output. simplex.GetWitnessPoints(&output->pointA, &output->pointB); output->distance = b2Distance(output->pointA, output->pointB); output->iterations = iter; // Cache the simplex. simplex.WriteCache(cache); // Apply radii if requested. if (input->useRadii) { float32 rA = proxyA->m_radius; float32 rB = proxyB->m_radius; if (output->distance > rA + rB && output->distance > b2_epsilon) { // Shapes are still no overlapped. // Move the witness points to the outer surface. output->distance -= rA + rB; b2Vec2 normal = output->pointB - output->pointA; normal.Normalize(); output->pointA += rA * normal; output->pointB -= rB * normal; } else { // Shapes are overlapped when radii are considered. // Move the witness points to the middle. b2Vec2 p = 0.5f * (output->pointA + output->pointB); output->pointA = p; output->pointB = p; output->distance = 0.0f; } } } ================================================ FILE: app/src/main/cpp/b2DistanceJoint.cpp ================================================ /* * Copyright (c) 2006-2011 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2DistanceJoint.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Body.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2TimeStep.h" // 1-D constrained system // m (v2 - v1) = lambda // v2 + (beta/h) * x1 + gamma * lambda = 0, gamma has units of inverse mass. // x2 = x1 + h * v2 // 1-D mass-damper-spring system // m (v2 - v1) + h * d * v2 + h * k * // C = norm(p2 - p1) - L // u = (p2 - p1) / norm(p2 - p1) // Cdot = dot(u, v2 + cross(w2, r2) - v1 - cross(w1, r1)) // J = [-u -cross(r1, u) u cross(r2, u)] // K = J * invM * JT // = invMass1 + invI1 * cross(r1, u)^2 + invMass2 + invI2 * cross(r2, u)^2 void b2DistanceJointDef::Initialize(b2Body* b1, b2Body* b2, const b2Vec2& anchor1, const b2Vec2& anchor2) { bodyA = b1; bodyB = b2; localAnchorA = bodyA->GetLocalPoint(anchor1); localAnchorB = bodyB->GetLocalPoint(anchor2); b2Vec2 d = anchor2 - anchor1; length = d.Length(); } b2DistanceJoint::b2DistanceJoint(const b2DistanceJointDef* def) : b2Joint(def) { m_localAnchorA = def->localAnchorA; m_localAnchorB = def->localAnchorB; m_length = def->length; m_frequencyHz = def->frequencyHz; m_dampingRatio = def->dampingRatio; m_impulse = 0.0f; m_gamma = 0.0f; m_bias = 0.0f; } void b2DistanceJoint::InitVelocityConstraints(const b2SolverData& data) { m_indexA = m_bodyA->m_islandIndex; m_indexB = m_bodyB->m_islandIndex; m_localCenterA = m_bodyA->m_sweep.localCenter; m_localCenterB = m_bodyB->m_sweep.localCenter; m_invMassA = m_bodyA->m_invMass; m_invMassB = m_bodyB->m_invMass; m_invIA = m_bodyA->m_invI; m_invIB = m_bodyB->m_invI; b2Vec2 cA = data.positions[m_indexA].c; float32 aA = data.positions[m_indexA].a; b2Vec2 vA = data.velocities[m_indexA].v; float32 wA = data.velocities[m_indexA].w; b2Vec2 cB = data.positions[m_indexB].c; float32 aB = data.positions[m_indexB].a; b2Vec2 vB = data.velocities[m_indexB].v; float32 wB = data.velocities[m_indexB].w; b2Rot qA(aA), qB(aB); m_rA = b2Mul(qA, m_localAnchorA - m_localCenterA); m_rB = b2Mul(qB, m_localAnchorB - m_localCenterB); m_u = cB + m_rB - cA - m_rA; // Handle singularity. float32 length = m_u.Length(); if (length > b2_linearSlop) { m_u *= 1.0f / length; } else { m_u.Set(0.0f, 0.0f); } float32 crAu = b2Cross(m_rA, m_u); float32 crBu = b2Cross(m_rB, m_u); float32 invMass = m_invMassA + m_invIA * crAu * crAu + m_invMassB + m_invIB * crBu * crBu; // Compute the effective mass matrix. m_mass = invMass != 0.0f ? 1.0f / invMass : 0.0f; if (m_frequencyHz > 0.0f) { float32 C = length - m_length; // Frequency float32 omega = 2.0f * b2_pi * m_frequencyHz; // Damping coefficient float32 d = 2.0f * m_mass * m_dampingRatio * omega; // Spring stiffness float32 k = m_mass * omega * omega; // magic formulas float32 h = data.step.dt; m_gamma = h * (d + h * k); m_gamma = m_gamma != 0.0f ? 1.0f / m_gamma : 0.0f; m_bias = C * h * k * m_gamma; invMass += m_gamma; m_mass = invMass != 0.0f ? 1.0f / invMass : 0.0f; } else { m_gamma = 0.0f; m_bias = 0.0f; } if (data.step.warmStarting) { // Scale the impulse to support a variable time step. m_impulse *= data.step.dtRatio; b2Vec2 P = m_impulse * m_u; vA -= m_invMassA * P; wA -= m_invIA * b2Cross(m_rA, P); vB += m_invMassB * P; wB += m_invIB * b2Cross(m_rB, P); } else { m_impulse = 0.0f; } data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; } void b2DistanceJoint::SolveVelocityConstraints(const b2SolverData& data) { b2Vec2 vA = data.velocities[m_indexA].v; float32 wA = data.velocities[m_indexA].w; b2Vec2 vB = data.velocities[m_indexB].v; float32 wB = data.velocities[m_indexB].w; // Cdot = dot(u, v + cross(w, r)) b2Vec2 vpA = vA + b2Cross(wA, m_rA); b2Vec2 vpB = vB + b2Cross(wB, m_rB); float32 Cdot = b2Dot(m_u, vpB - vpA); float32 impulse = -m_mass * (Cdot + m_bias + m_gamma * m_impulse); m_impulse += impulse; b2Vec2 P = impulse * m_u; vA -= m_invMassA * P; wA -= m_invIA * b2Cross(m_rA, P); vB += m_invMassB * P; wB += m_invIB * b2Cross(m_rB, P); data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; } bool b2DistanceJoint::SolvePositionConstraints(const b2SolverData& data) { if (m_frequencyHz > 0.0f) { // There is no position correction for soft distance constraints. return true; } b2Vec2 cA = data.positions[m_indexA].c; float32 aA = data.positions[m_indexA].a; b2Vec2 cB = data.positions[m_indexB].c; float32 aB = data.positions[m_indexB].a; b2Rot qA(aA), qB(aB); b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA); b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB); b2Vec2 u = cB + rB - cA - rA; float32 length = u.Normalize(); float32 C = length - m_length; C = b2Clamp(C, -b2_maxLinearCorrection, b2_maxLinearCorrection); float32 impulse = -m_mass * C; b2Vec2 P = impulse * u; cA -= m_invMassA * P; aA -= m_invIA * b2Cross(rA, P); cB += m_invMassB * P; aB += m_invIB * b2Cross(rB, P); data.positions[m_indexA].c = cA; data.positions[m_indexA].a = aA; data.positions[m_indexB].c = cB; data.positions[m_indexB].a = aB; return b2Abs(C) < b2_linearSlop; } b2Vec2 b2DistanceJoint::GetAnchorA() const { return m_bodyA->GetWorldPoint(m_localAnchorA); } b2Vec2 b2DistanceJoint::GetAnchorB() const { return m_bodyB->GetWorldPoint(m_localAnchorB); } b2Vec2 b2DistanceJoint::GetReactionForce(float32 inv_dt) const { b2Vec2 F = (inv_dt * m_impulse) * m_u; return F; } float32 b2DistanceJoint::GetReactionTorque(float32 inv_dt) const { B2_NOT_USED(inv_dt); return 0.0f; } void b2DistanceJoint::Dump() { int32 indexA = m_bodyA->m_islandIndex; int32 indexB = m_bodyB->m_islandIndex; b2Log(" b2DistanceJointDef jd;\n"); b2Log(" jd.bodyA = bodies[%d];\n", indexA); b2Log(" jd.bodyB = bodies[%d];\n", indexB); b2Log(" jd.collideConnected = bool(%d);\n", m_collideConnected); b2Log(" jd.localAnchorA.Set(%.15lef, %.15lef);\n", m_localAnchorA.x, m_localAnchorA.y); b2Log(" jd.localAnchorB.Set(%.15lef, %.15lef);\n", m_localAnchorB.x, m_localAnchorB.y); b2Log(" jd.length = %.15lef;\n", m_length); b2Log(" jd.frequencyHz = %.15lef;\n", m_frequencyHz); b2Log(" jd.dampingRatio = %.15lef;\n", m_dampingRatio); b2Log(" joints[%d] = m_world->CreateJoint(&jd);\n", m_index); } ================================================ FILE: app/src/main/cpp/b2Draw.cpp ================================================ /* * Copyright (c) 2011 Erin Catto http://box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Common/b2Draw.h" b2Draw::b2Draw() { m_drawFlags = 0; } void b2Draw::SetFlags(uint32 flags) { m_drawFlags = flags; } uint32 b2Draw::GetFlags() const { return m_drawFlags; } void b2Draw::AppendFlags(uint32 flags) { m_drawFlags |= flags; } void b2Draw::ClearFlags(uint32 flags) { m_drawFlags &= ~flags; } ================================================ FILE: app/src/main/cpp/b2DynamicTree.cpp ================================================ /* * Copyright (c) 2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Collision/b2DynamicTree.h" #include #include b2DynamicTree::b2DynamicTree() { m_root = b2_nullNode; m_nodeCapacity = 16; m_nodeCount = 0; m_nodes = (b2TreeNode*)b2Alloc(m_nodeCapacity * sizeof(b2TreeNode)); memset(m_nodes, 0, m_nodeCapacity * sizeof(b2TreeNode)); // Build a linked list for the free list. for (int32 i = 0; i < m_nodeCapacity - 1; ++i) { m_nodes[i].next = i + 1; m_nodes[i].height = -1; } m_nodes[m_nodeCapacity-1].next = b2_nullNode; m_nodes[m_nodeCapacity-1].height = -1; m_freeList = 0; m_path = 0; m_insertionCount = 0; } b2DynamicTree::~b2DynamicTree() { // This frees the entire tree in one shot. b2Free(m_nodes); } // Allocate a node from the pool. Grow the pool if necessary. int32 b2DynamicTree::AllocateNode() { // Expand the node pool as needed. if (m_freeList == b2_nullNode) { b2Assert(m_nodeCount == m_nodeCapacity); // The free list is empty. Rebuild a bigger pool. b2TreeNode* oldNodes = m_nodes; m_nodeCapacity *= 2; m_nodes = (b2TreeNode*)b2Alloc(m_nodeCapacity * sizeof(b2TreeNode)); memcpy(m_nodes, oldNodes, m_nodeCount * sizeof(b2TreeNode)); b2Free(oldNodes); // Build a linked list for the free list. The parent // pointer becomes the "next" pointer. for (int32 i = m_nodeCount; i < m_nodeCapacity - 1; ++i) { m_nodes[i].next = i + 1; m_nodes[i].height = -1; } m_nodes[m_nodeCapacity-1].next = b2_nullNode; m_nodes[m_nodeCapacity-1].height = -1; m_freeList = m_nodeCount; } // Peel a node off the free list. int32 nodeId = m_freeList; m_freeList = m_nodes[nodeId].next; m_nodes[nodeId].parent = b2_nullNode; m_nodes[nodeId].child1 = b2_nullNode; m_nodes[nodeId].child2 = b2_nullNode; m_nodes[nodeId].height = 0; m_nodes[nodeId].userData = NULL; ++m_nodeCount; return nodeId; } // Return a node to the pool. void b2DynamicTree::FreeNode(int32 nodeId) { b2Assert(0 <= nodeId && nodeId < m_nodeCapacity); b2Assert(0 < m_nodeCount); m_nodes[nodeId].next = m_freeList; m_nodes[nodeId].height = -1; m_freeList = nodeId; --m_nodeCount; } // Create a proxy in the tree as a leaf node. We return the index // of the node instead of a pointer so that we can grow // the node pool. int32 b2DynamicTree::CreateProxy(const b2AABB& aabb, void* userData) { int32 proxyId = AllocateNode(); // Fatten the aabb. b2Vec2 r(b2_aabbExtension, b2_aabbExtension); m_nodes[proxyId].aabb.lowerBound = aabb.lowerBound - r; m_nodes[proxyId].aabb.upperBound = aabb.upperBound + r; m_nodes[proxyId].userData = userData; m_nodes[proxyId].height = 0; InsertLeaf(proxyId); return proxyId; } void b2DynamicTree::DestroyProxy(int32 proxyId) { b2Assert(0 <= proxyId && proxyId < m_nodeCapacity); b2Assert(m_nodes[proxyId].IsLeaf()); RemoveLeaf(proxyId); FreeNode(proxyId); } bool b2DynamicTree::MoveProxy(int32 proxyId, const b2AABB& aabb, const b2Vec2& displacement) { b2Assert(0 <= proxyId && proxyId < m_nodeCapacity); b2Assert(m_nodes[proxyId].IsLeaf()); if (m_nodes[proxyId].aabb.Contains(aabb)) { return false; } RemoveLeaf(proxyId); // Extend AABB. b2AABB b = aabb; b2Vec2 r(b2_aabbExtension, b2_aabbExtension); b.lowerBound = b.lowerBound - r; b.upperBound = b.upperBound + r; // Predict AABB displacement. b2Vec2 d = b2_aabbMultiplier * displacement; if (d.x < 0.0f) { b.lowerBound.x += d.x; } else { b.upperBound.x += d.x; } if (d.y < 0.0f) { b.lowerBound.y += d.y; } else { b.upperBound.y += d.y; } m_nodes[proxyId].aabb = b; InsertLeaf(proxyId); return true; } void b2DynamicTree::InsertLeaf(int32 leaf) { ++m_insertionCount; if (m_root == b2_nullNode) { m_root = leaf; m_nodes[m_root].parent = b2_nullNode; return; } // Find the best sibling for this node b2AABB leafAABB = m_nodes[leaf].aabb; int32 index = m_root; while (m_nodes[index].IsLeaf() == false) { int32 child1 = m_nodes[index].child1; int32 child2 = m_nodes[index].child2; float32 area = m_nodes[index].aabb.GetPerimeter(); b2AABB combinedAABB; combinedAABB.Combine(m_nodes[index].aabb, leafAABB); float32 combinedArea = combinedAABB.GetPerimeter(); // Cost of creating a new parent for this node and the new leaf float32 cost = 2.0f * combinedArea; // Minimum cost of pushing the leaf further down the tree float32 inheritanceCost = 2.0f * (combinedArea - area); // Cost of descending into child1 float32 cost1; if (m_nodes[child1].IsLeaf()) { b2AABB aabb; aabb.Combine(leafAABB, m_nodes[child1].aabb); cost1 = aabb.GetPerimeter() + inheritanceCost; } else { b2AABB aabb; aabb.Combine(leafAABB, m_nodes[child1].aabb); float32 oldArea = m_nodes[child1].aabb.GetPerimeter(); float32 newArea = aabb.GetPerimeter(); cost1 = (newArea - oldArea) + inheritanceCost; } // Cost of descending into child2 float32 cost2; if (m_nodes[child2].IsLeaf()) { b2AABB aabb; aabb.Combine(leafAABB, m_nodes[child2].aabb); cost2 = aabb.GetPerimeter() + inheritanceCost; } else { b2AABB aabb; aabb.Combine(leafAABB, m_nodes[child2].aabb); float32 oldArea = m_nodes[child2].aabb.GetPerimeter(); float32 newArea = aabb.GetPerimeter(); cost2 = newArea - oldArea + inheritanceCost; } // Descend according to the minimum cost. if (cost < cost1 && cost < cost2) { break; } // Descend if (cost1 < cost2) { index = child1; } else { index = child2; } } int32 sibling = index; // Create a new parent. int32 oldParent = m_nodes[sibling].parent; int32 newParent = AllocateNode(); m_nodes[newParent].parent = oldParent; m_nodes[newParent].userData = NULL; m_nodes[newParent].aabb.Combine(leafAABB, m_nodes[sibling].aabb); m_nodes[newParent].height = m_nodes[sibling].height + 1; if (oldParent != b2_nullNode) { // The sibling was not the root. if (m_nodes[oldParent].child1 == sibling) { m_nodes[oldParent].child1 = newParent; } else { m_nodes[oldParent].child2 = newParent; } m_nodes[newParent].child1 = sibling; m_nodes[newParent].child2 = leaf; m_nodes[sibling].parent = newParent; m_nodes[leaf].parent = newParent; } else { // The sibling was the root. m_nodes[newParent].child1 = sibling; m_nodes[newParent].child2 = leaf; m_nodes[sibling].parent = newParent; m_nodes[leaf].parent = newParent; m_root = newParent; } // Walk back up the tree fixing heights and AABBs index = m_nodes[leaf].parent; while (index != b2_nullNode) { index = Balance(index); int32 child1 = m_nodes[index].child1; int32 child2 = m_nodes[index].child2; b2Assert(child1 != b2_nullNode); b2Assert(child2 != b2_nullNode); m_nodes[index].height = 1 + b2Max(m_nodes[child1].height, m_nodes[child2].height); m_nodes[index].aabb.Combine(m_nodes[child1].aabb, m_nodes[child2].aabb); index = m_nodes[index].parent; } //Validate(); } void b2DynamicTree::RemoveLeaf(int32 leaf) { if (leaf == m_root) { m_root = b2_nullNode; return; } int32 parent = m_nodes[leaf].parent; int32 grandParent = m_nodes[parent].parent; int32 sibling; if (m_nodes[parent].child1 == leaf) { sibling = m_nodes[parent].child2; } else { sibling = m_nodes[parent].child1; } if (grandParent != b2_nullNode) { // Destroy parent and connect sibling to grandParent. if (m_nodes[grandParent].child1 == parent) { m_nodes[grandParent].child1 = sibling; } else { m_nodes[grandParent].child2 = sibling; } m_nodes[sibling].parent = grandParent; FreeNode(parent); // Adjust ancestor bounds. int32 index = grandParent; while (index != b2_nullNode) { index = Balance(index); int32 child1 = m_nodes[index].child1; int32 child2 = m_nodes[index].child2; m_nodes[index].aabb.Combine(m_nodes[child1].aabb, m_nodes[child2].aabb); m_nodes[index].height = 1 + b2Max(m_nodes[child1].height, m_nodes[child2].height); index = m_nodes[index].parent; } } else { m_root = sibling; m_nodes[sibling].parent = b2_nullNode; FreeNode(parent); } //Validate(); } // Perform a left or right rotation if node A is imbalanced. // Returns the new root index. int32 b2DynamicTree::Balance(int32 iA) { b2Assert(iA != b2_nullNode); b2TreeNode* A = m_nodes + iA; if (A->IsLeaf() || A->height < 2) { return iA; } int32 iB = A->child1; int32 iC = A->child2; b2Assert(0 <= iB && iB < m_nodeCapacity); b2Assert(0 <= iC && iC < m_nodeCapacity); b2TreeNode* B = m_nodes + iB; b2TreeNode* C = m_nodes + iC; int32 balance = C->height - B->height; // Rotate C up if (balance > 1) { int32 iF = C->child1; int32 iG = C->child2; b2TreeNode* F = m_nodes + iF; b2TreeNode* G = m_nodes + iG; b2Assert(0 <= iF && iF < m_nodeCapacity); b2Assert(0 <= iG && iG < m_nodeCapacity); // Swap A and C C->child1 = iA; C->parent = A->parent; A->parent = iC; // A's old parent should point to C if (C->parent != b2_nullNode) { if (m_nodes[C->parent].child1 == iA) { m_nodes[C->parent].child1 = iC; } else { b2Assert(m_nodes[C->parent].child2 == iA); m_nodes[C->parent].child2 = iC; } } else { m_root = iC; } // Rotate if (F->height > G->height) { C->child2 = iF; A->child2 = iG; G->parent = iA; A->aabb.Combine(B->aabb, G->aabb); C->aabb.Combine(A->aabb, F->aabb); A->height = 1 + b2Max(B->height, G->height); C->height = 1 + b2Max(A->height, F->height); } else { C->child2 = iG; A->child2 = iF; F->parent = iA; A->aabb.Combine(B->aabb, F->aabb); C->aabb.Combine(A->aabb, G->aabb); A->height = 1 + b2Max(B->height, F->height); C->height = 1 + b2Max(A->height, G->height); } return iC; } // Rotate B up if (balance < -1) { int32 iD = B->child1; int32 iE = B->child2; b2TreeNode* D = m_nodes + iD; b2TreeNode* E = m_nodes + iE; b2Assert(0 <= iD && iD < m_nodeCapacity); b2Assert(0 <= iE && iE < m_nodeCapacity); // Swap A and B B->child1 = iA; B->parent = A->parent; A->parent = iB; // A's old parent should point to B if (B->parent != b2_nullNode) { if (m_nodes[B->parent].child1 == iA) { m_nodes[B->parent].child1 = iB; } else { b2Assert(m_nodes[B->parent].child2 == iA); m_nodes[B->parent].child2 = iB; } } else { m_root = iB; } // Rotate if (D->height > E->height) { B->child2 = iD; A->child1 = iE; E->parent = iA; A->aabb.Combine(C->aabb, E->aabb); B->aabb.Combine(A->aabb, D->aabb); A->height = 1 + b2Max(C->height, E->height); B->height = 1 + b2Max(A->height, D->height); } else { B->child2 = iE; A->child1 = iD; D->parent = iA; A->aabb.Combine(C->aabb, D->aabb); B->aabb.Combine(A->aabb, E->aabb); A->height = 1 + b2Max(C->height, D->height); B->height = 1 + b2Max(A->height, E->height); } return iB; } return iA; } int32 b2DynamicTree::GetHeight() const { if (m_root == b2_nullNode) { return 0; } return m_nodes[m_root].height; } // float32 b2DynamicTree::GetAreaRatio() const { if (m_root == b2_nullNode) { return 0.0f; } const b2TreeNode* root = m_nodes + m_root; float32 rootArea = root->aabb.GetPerimeter(); float32 totalArea = 0.0f; for (int32 i = 0; i < m_nodeCapacity; ++i) { const b2TreeNode* node = m_nodes + i; if (node->height < 0) { // Free node in pool continue; } totalArea += node->aabb.GetPerimeter(); } return totalArea / rootArea; } // Compute the height of a sub-tree. int32 b2DynamicTree::ComputeHeight(int32 nodeId) const { b2Assert(0 <= nodeId && nodeId < m_nodeCapacity); b2TreeNode* node = m_nodes + nodeId; if (node->IsLeaf()) { return 0; } int32 height1 = ComputeHeight(node->child1); int32 height2 = ComputeHeight(node->child2); return 1 + b2Max(height1, height2); } int32 b2DynamicTree::ComputeHeight() const { int32 height = ComputeHeight(m_root); return height; } void b2DynamicTree::ValidateStructure(int32 index) const { if (index == b2_nullNode) { return; } if (index == m_root) { b2Assert(m_nodes[index].parent == b2_nullNode); } const b2TreeNode* node = m_nodes + index; int32 child1 = node->child1; int32 child2 = node->child2; if (node->IsLeaf()) { b2Assert(child1 == b2_nullNode); b2Assert(child2 == b2_nullNode); b2Assert(node->height == 0); return; } b2Assert(0 <= child1 && child1 < m_nodeCapacity); b2Assert(0 <= child2 && child2 < m_nodeCapacity); b2Assert(m_nodes[child1].parent == index); b2Assert(m_nodes[child2].parent == index); ValidateStructure(child1); ValidateStructure(child2); } void b2DynamicTree::ValidateMetrics(int32 index) const { if (index == b2_nullNode) { return; } const b2TreeNode* node = m_nodes + index; int32 child1 = node->child1; int32 child2 = node->child2; if (node->IsLeaf()) { b2Assert(child1 == b2_nullNode); b2Assert(child2 == b2_nullNode); b2Assert(node->height == 0); return; } b2Assert(0 <= child1 && child1 < m_nodeCapacity); b2Assert(0 <= child2 && child2 < m_nodeCapacity); int32 height1 = m_nodes[child1].height; int32 height2 = m_nodes[child2].height; int32 height; height = 1 + b2Max(height1, height2); b2Assert(node->height == height); b2AABB aabb; aabb.Combine(m_nodes[child1].aabb, m_nodes[child2].aabb); b2Assert(aabb.lowerBound == node->aabb.lowerBound); b2Assert(aabb.upperBound == node->aabb.upperBound); ValidateMetrics(child1); ValidateMetrics(child2); } void b2DynamicTree::Validate() const { ValidateStructure(m_root); ValidateMetrics(m_root); int32 freeCount = 0; int32 freeIndex = m_freeList; while (freeIndex != b2_nullNode) { b2Assert(0 <= freeIndex && freeIndex < m_nodeCapacity); freeIndex = m_nodes[freeIndex].next; ++freeCount; } b2Assert(GetHeight() == ComputeHeight()); b2Assert(m_nodeCount + freeCount == m_nodeCapacity); } int32 b2DynamicTree::GetMaxBalance() const { int32 maxBalance = 0; for (int32 i = 0; i < m_nodeCapacity; ++i) { const b2TreeNode* node = m_nodes + i; if (node->height <= 1) { continue; } b2Assert(node->IsLeaf() == false); int32 child1 = node->child1; int32 child2 = node->child2; int32 balance = b2Abs(m_nodes[child2].height - m_nodes[child1].height); maxBalance = b2Max(maxBalance, balance); } return maxBalance; } void b2DynamicTree::RebuildBottomUp() { int32* nodes = (int32*)b2Alloc(m_nodeCount * sizeof(int32)); int32 count = 0; // Build array of leaves. Free the rest. for (int32 i = 0; i < m_nodeCapacity; ++i) { if (m_nodes[i].height < 0) { // free node in pool continue; } if (m_nodes[i].IsLeaf()) { m_nodes[i].parent = b2_nullNode; nodes[count] = i; ++count; } else { FreeNode(i); } } while (count > 1) { float32 minCost = b2_maxFloat; int32 iMin = -1, jMin = -1; for (int32 i = 0; i < count; ++i) { b2AABB aabbi = m_nodes[nodes[i]].aabb; for (int32 j = i + 1; j < count; ++j) { b2AABB aabbj = m_nodes[nodes[j]].aabb; b2AABB b; b.Combine(aabbi, aabbj); float32 cost = b.GetPerimeter(); if (cost < minCost) { iMin = i; jMin = j; minCost = cost; } } } int32 index1 = nodes[iMin]; int32 index2 = nodes[jMin]; b2TreeNode* child1 = m_nodes + index1; b2TreeNode* child2 = m_nodes + index2; int32 parentIndex = AllocateNode(); b2TreeNode* parent = m_nodes + parentIndex; parent->child1 = index1; parent->child2 = index2; parent->height = 1 + b2Max(child1->height, child2->height); parent->aabb.Combine(child1->aabb, child2->aabb); parent->parent = b2_nullNode; child1->parent = parentIndex; child2->parent = parentIndex; nodes[jMin] = nodes[count-1]; nodes[iMin] = parentIndex; --count; } m_root = nodes[0]; b2Free(nodes); Validate(); } void b2DynamicTree::ShiftOrigin(const b2Vec2& newOrigin) { // Build array of leaves. Free the rest. for (int32 i = 0; i < m_nodeCapacity; ++i) { m_nodes[i].aabb.lowerBound -= newOrigin; m_nodes[i].aabb.upperBound -= newOrigin; } } ================================================ FILE: app/src/main/cpp/b2EdgeAndCircleContact.cpp ================================================ /* * Copyright (c) 2006-2010 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Dynamics/Contacts/b2EdgeAndCircleContact.h" #include "isEngine/ext_lib/Box2D/Common/b2BlockAllocator.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Fixture.h" #include b2Contact* b2EdgeAndCircleContact::Create(b2Fixture* fixtureA, int32, b2Fixture* fixtureB, int32, b2BlockAllocator* allocator) { void* mem = allocator->Allocate(sizeof(b2EdgeAndCircleContact)); return new (mem) b2EdgeAndCircleContact(fixtureA, fixtureB); } void b2EdgeAndCircleContact::Destroy(b2Contact* contact, b2BlockAllocator* allocator) { ((b2EdgeAndCircleContact*)contact)->~b2EdgeAndCircleContact(); allocator->Free(contact, sizeof(b2EdgeAndCircleContact)); } b2EdgeAndCircleContact::b2EdgeAndCircleContact(b2Fixture* fixtureA, b2Fixture* fixtureB) : b2Contact(fixtureA, 0, fixtureB, 0) { b2Assert(m_fixtureA->GetType() == b2Shape::e_edge); b2Assert(m_fixtureB->GetType() == b2Shape::e_circle); } void b2EdgeAndCircleContact::Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB) { b2CollideEdgeAndCircle( manifold, (b2EdgeShape*)m_fixtureA->GetShape(), xfA, (b2CircleShape*)m_fixtureB->GetShape(), xfB); } ================================================ FILE: app/src/main/cpp/b2EdgeAndPolygonContact.cpp ================================================ /* * Copyright (c) 2006-2010 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Dynamics/Contacts/b2EdgeAndPolygonContact.h" #include "isEngine/ext_lib/Box2D/Common/b2BlockAllocator.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Fixture.h" #include b2Contact* b2EdgeAndPolygonContact::Create(b2Fixture* fixtureA, int32, b2Fixture* fixtureB, int32, b2BlockAllocator* allocator) { void* mem = allocator->Allocate(sizeof(b2EdgeAndPolygonContact)); return new (mem) b2EdgeAndPolygonContact(fixtureA, fixtureB); } void b2EdgeAndPolygonContact::Destroy(b2Contact* contact, b2BlockAllocator* allocator) { ((b2EdgeAndPolygonContact*)contact)->~b2EdgeAndPolygonContact(); allocator->Free(contact, sizeof(b2EdgeAndPolygonContact)); } b2EdgeAndPolygonContact::b2EdgeAndPolygonContact(b2Fixture* fixtureA, b2Fixture* fixtureB) : b2Contact(fixtureA, 0, fixtureB, 0) { b2Assert(m_fixtureA->GetType() == b2Shape::e_edge); b2Assert(m_fixtureB->GetType() == b2Shape::e_polygon); } void b2EdgeAndPolygonContact::Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB) { b2CollideEdgeAndPolygon( manifold, (b2EdgeShape*)m_fixtureA->GetShape(), xfA, (b2PolygonShape*)m_fixtureB->GetShape(), xfB); } ================================================ FILE: app/src/main/cpp/b2EdgeShape.cpp ================================================ /* * Copyright (c) 2006-2010 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2EdgeShape.h" #include void b2EdgeShape::Set(const b2Vec2& v1, const b2Vec2& v2) { m_vertex1 = v1; m_vertex2 = v2; m_hasVertex0 = false; m_hasVertex3 = false; } b2Shape* b2EdgeShape::Clone(b2BlockAllocator* allocator) const { void* mem = allocator->Allocate(sizeof(b2EdgeShape)); b2EdgeShape* clone = new (mem) b2EdgeShape; *clone = *this; return clone; } int32 b2EdgeShape::GetChildCount() const { return 1; } bool b2EdgeShape::TestPoint(const b2Transform& xf, const b2Vec2& p) const { B2_NOT_USED(xf); B2_NOT_USED(p); return false; } // p = p1 + t * d // v = v1 + s * e // p1 + t * d = v1 + s * e // s * e - t * d = p1 - v1 bool b2EdgeShape::RayCast(b2RayCastOutput* output, const b2RayCastInput& input, const b2Transform& xf, int32 childIndex) const { B2_NOT_USED(childIndex); // Put the ray into the edge's frame of reference. b2Vec2 p1 = b2MulT(xf.q, input.p1 - xf.p); b2Vec2 p2 = b2MulT(xf.q, input.p2 - xf.p); b2Vec2 d = p2 - p1; b2Vec2 v1 = m_vertex1; b2Vec2 v2 = m_vertex2; b2Vec2 e = v2 - v1; b2Vec2 normal(e.y, -e.x); normal.Normalize(); // q = p1 + t * d // dot(normal, q - v1) = 0 // dot(normal, p1 - v1) + t * dot(normal, d) = 0 float32 numerator = b2Dot(normal, v1 - p1); float32 denominator = b2Dot(normal, d); if (denominator == 0.0f) { return false; } float32 t = numerator / denominator; if (t < 0.0f || input.maxFraction < t) { return false; } b2Vec2 q = p1 + t * d; // q = v1 + s * r // s = dot(q - v1, r) / dot(r, r) b2Vec2 r = v2 - v1; float32 rr = b2Dot(r, r); if (rr == 0.0f) { return false; } float32 s = b2Dot(q - v1, r) / rr; if (s < 0.0f || 1.0f < s) { return false; } output->fraction = t; if (numerator > 0.0f) { output->normal = -b2Mul(xf.q, normal); } else { output->normal = b2Mul(xf.q, normal); } return true; } void b2EdgeShape::ComputeAABB(b2AABB* aabb, const b2Transform& xf, int32 childIndex) const { B2_NOT_USED(childIndex); b2Vec2 v1 = b2Mul(xf, m_vertex1); b2Vec2 v2 = b2Mul(xf, m_vertex2); b2Vec2 lower = b2Min(v1, v2); b2Vec2 upper = b2Max(v1, v2); b2Vec2 r(m_radius, m_radius); aabb->lowerBound = lower - r; aabb->upperBound = upper + r; } void b2EdgeShape::ComputeMass(b2MassData* massData, float32 density) const { B2_NOT_USED(density); massData->mass = 0.0f; massData->center = 0.5f * (m_vertex1 + m_vertex2); massData->I = 0.0f; } ================================================ FILE: app/src/main/cpp/b2Fixture.cpp ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Dynamics/b2Fixture.h" #include "isEngine/ext_lib/Box2D/Dynamics/Contacts/b2Contact.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2World.h" #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2CircleShape.h" #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2EdgeShape.h" #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2PolygonShape.h" #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2ChainShape.h" #include "isEngine/ext_lib/Box2D/Collision/b2BroadPhase.h" #include "isEngine/ext_lib/Box2D/Collision/b2Collision.h" #include "isEngine/ext_lib/Box2D/Common/b2BlockAllocator.h" b2Fixture::b2Fixture() { m_userData = NULL; m_body = NULL; m_next = NULL; m_proxies = NULL; m_proxyCount = 0; m_shape = NULL; m_density = 0.0f; } void b2Fixture::Create(b2BlockAllocator* allocator, b2Body* body, const b2FixtureDef* def) { m_userData = def->userData; m_friction = def->friction; m_restitution = def->restitution; m_body = body; m_next = NULL; m_filter = def->filter; m_isSensor = def->isSensor; m_shape = def->shape->Clone(allocator); // Reserve proxy space int32 childCount = m_shape->GetChildCount(); m_proxies = (b2FixtureProxy*)allocator->Allocate(childCount * sizeof(b2FixtureProxy)); for (int32 i = 0; i < childCount; ++i) { m_proxies[i].fixture = NULL; m_proxies[i].proxyId = b2BroadPhase::e_nullProxy; } m_proxyCount = 0; m_density = def->density; } void b2Fixture::Destroy(b2BlockAllocator* allocator) { // The proxies must be destroyed before calling this. b2Assert(m_proxyCount == 0); // Free the proxy array. int32 childCount = m_shape->GetChildCount(); allocator->Free(m_proxies, childCount * sizeof(b2FixtureProxy)); m_proxies = NULL; // Free the child shape. switch (m_shape->m_type) { case b2Shape::e_circle: { b2CircleShape* s = (b2CircleShape*)m_shape; s->~b2CircleShape(); allocator->Free(s, sizeof(b2CircleShape)); } break; case b2Shape::e_edge: { b2EdgeShape* s = (b2EdgeShape*)m_shape; s->~b2EdgeShape(); allocator->Free(s, sizeof(b2EdgeShape)); } break; case b2Shape::e_polygon: { b2PolygonShape* s = (b2PolygonShape*)m_shape; s->~b2PolygonShape(); allocator->Free(s, sizeof(b2PolygonShape)); } break; case b2Shape::e_chain: { b2ChainShape* s = (b2ChainShape*)m_shape; s->~b2ChainShape(); allocator->Free(s, sizeof(b2ChainShape)); } break; default: b2Assert(false); break; } m_shape = NULL; } void b2Fixture::CreateProxies(b2BroadPhase* broadPhase, const b2Transform& xf) { b2Assert(m_proxyCount == 0); // Create proxies in the broad-phase. m_proxyCount = m_shape->GetChildCount(); for (int32 i = 0; i < m_proxyCount; ++i) { b2FixtureProxy* proxy = m_proxies + i; m_shape->ComputeAABB(&proxy->aabb, xf, i); proxy->proxyId = broadPhase->CreateProxy(proxy->aabb, proxy); proxy->fixture = this; proxy->childIndex = i; } } void b2Fixture::DestroyProxies(b2BroadPhase* broadPhase) { // Destroy proxies in the broad-phase. for (int32 i = 0; i < m_proxyCount; ++i) { b2FixtureProxy* proxy = m_proxies + i; broadPhase->DestroyProxy(proxy->proxyId); proxy->proxyId = b2BroadPhase::e_nullProxy; } m_proxyCount = 0; } void b2Fixture::Synchronize(b2BroadPhase* broadPhase, const b2Transform& transform1, const b2Transform& transform2) { if (m_proxyCount == 0) { return; } for (int32 i = 0; i < m_proxyCount; ++i) { b2FixtureProxy* proxy = m_proxies + i; // Compute an AABB that covers the swept shape (may miss some rotation effect). b2AABB aabb1, aabb2; m_shape->ComputeAABB(&aabb1, transform1, proxy->childIndex); m_shape->ComputeAABB(&aabb2, transform2, proxy->childIndex); proxy->aabb.Combine(aabb1, aabb2); b2Vec2 displacement = transform2.p - transform1.p; broadPhase->MoveProxy(proxy->proxyId, proxy->aabb, displacement); } } void b2Fixture::SetFilterData(const b2Filter& filter) { m_filter = filter; Refilter(); } void b2Fixture::Refilter() { if (m_body == NULL) { return; } // Flag associated contacts for filtering. b2ContactEdge* edge = m_body->GetContactList(); while (edge) { b2Contact* contact = edge->contact; b2Fixture* fixtureA = contact->GetFixtureA(); b2Fixture* fixtureB = contact->GetFixtureB(); if (fixtureA == this || fixtureB == this) { contact->FlagForFiltering(); } edge = edge->next; } b2World* world = m_body->GetWorld(); if (world == NULL) { return; } // Touch each proxy so that new pairs may be created b2BroadPhase* broadPhase = &world->m_contactManager.m_broadPhase; for (int32 i = 0; i < m_proxyCount; ++i) { broadPhase->TouchProxy(m_proxies[i].proxyId); } } void b2Fixture::SetSensor(bool sensor) { if (sensor != m_isSensor) { m_body->SetAwake(true); m_isSensor = sensor; } } void b2Fixture::Dump(int32 bodyIndex) { b2Log(" b2FixtureDef fd;\n"); b2Log(" fd.friction = %.15lef;\n", m_friction); b2Log(" fd.restitution = %.15lef;\n", m_restitution); b2Log(" fd.density = %.15lef;\n", m_density); b2Log(" fd.isSensor = bool(%d);\n", m_isSensor); b2Log(" fd.filter.categoryBits = uint16(%d);\n", m_filter.categoryBits); b2Log(" fd.filter.maskBits = uint16(%d);\n", m_filter.maskBits); b2Log(" fd.filter.groupIndex = int16(%d);\n", m_filter.groupIndex); switch (m_shape->m_type) { case b2Shape::e_circle: { b2CircleShape* s = (b2CircleShape*)m_shape; b2Log(" b2CircleShape shape;\n"); b2Log(" shape.m_radius = %.15lef;\n", s->m_radius); b2Log(" shape.m_p.Set(%.15lef, %.15lef);\n", s->m_p.x, s->m_p.y); } break; case b2Shape::e_edge: { b2EdgeShape* s = (b2EdgeShape*)m_shape; b2Log(" b2EdgeShape shape;\n"); b2Log(" shape.m_radius = %.15lef;\n", s->m_radius); b2Log(" shape.m_vertex0.Set(%.15lef, %.15lef);\n", s->m_vertex0.x, s->m_vertex0.y); b2Log(" shape.m_vertex1.Set(%.15lef, %.15lef);\n", s->m_vertex1.x, s->m_vertex1.y); b2Log(" shape.m_vertex2.Set(%.15lef, %.15lef);\n", s->m_vertex2.x, s->m_vertex2.y); b2Log(" shape.m_vertex3.Set(%.15lef, %.15lef);\n", s->m_vertex3.x, s->m_vertex3.y); b2Log(" shape.m_hasVertex0 = bool(%d);\n", s->m_hasVertex0); b2Log(" shape.m_hasVertex3 = bool(%d);\n", s->m_hasVertex3); } break; case b2Shape::e_polygon: { b2PolygonShape* s = (b2PolygonShape*)m_shape; b2Log(" b2PolygonShape shape;\n"); b2Log(" b2Vec2 vs[%d];\n", b2_maxPolygonVertices); for (int32 i = 0; i < s->m_count; ++i) { b2Log(" vs[%d].Set(%.15lef, %.15lef);\n", i, s->m_vertices[i].x, s->m_vertices[i].y); } b2Log(" shape.Set(vs, %d);\n", s->m_count); } break; case b2Shape::e_chain: { b2ChainShape* s = (b2ChainShape*)m_shape; b2Log(" b2ChainShape shape;\n"); b2Log(" b2Vec2 vs[%d];\n", s->m_count); for (int32 i = 0; i < s->m_count; ++i) { b2Log(" vs[%d].Set(%.15lef, %.15lef);\n", i, s->m_vertices[i].x, s->m_vertices[i].y); } b2Log(" shape.CreateChain(vs, %d);\n", s->m_count); b2Log(" shape.m_prevVertex.Set(%.15lef, %.15lef);\n", s->m_prevVertex.x, s->m_prevVertex.y); b2Log(" shape.m_nextVertex.Set(%.15lef, %.15lef);\n", s->m_nextVertex.x, s->m_nextVertex.y); b2Log(" shape.m_hasPrevVertex = bool(%d);\n", s->m_hasPrevVertex); b2Log(" shape.m_hasNextVertex = bool(%d);\n", s->m_hasNextVertex); } break; default: return; } b2Log("\n"); b2Log(" fd.shape = &shape;\n"); b2Log("\n"); b2Log(" bodies[%d]->CreateFixture(&fd);\n", bodyIndex); } ================================================ FILE: app/src/main/cpp/b2FrictionJoint.cpp ================================================ /* * Copyright (c) 2006-2011 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2FrictionJoint.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Body.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2TimeStep.h" // Point-to-point constraint // Cdot = v2 - v1 // = v2 + cross(w2, r2) - v1 - cross(w1, r1) // J = [-I -r1_skew I r2_skew ] // Identity used: // w k % (rx i + ry j) = w * (-ry i + rx j) // Angle constraint // Cdot = w2 - w1 // J = [0 0 -1 0 0 1] // K = invI1 + invI2 void b2FrictionJointDef::Initialize(b2Body* bA, b2Body* bB, const b2Vec2& anchor) { bodyA = bA; bodyB = bB; localAnchorA = bodyA->GetLocalPoint(anchor); localAnchorB = bodyB->GetLocalPoint(anchor); } b2FrictionJoint::b2FrictionJoint(const b2FrictionJointDef* def) : b2Joint(def) { m_localAnchorA = def->localAnchorA; m_localAnchorB = def->localAnchorB; m_linearImpulse.SetZero(); m_angularImpulse = 0.0f; m_maxForce = def->maxForce; m_maxTorque = def->maxTorque; } void b2FrictionJoint::InitVelocityConstraints(const b2SolverData& data) { m_indexA = m_bodyA->m_islandIndex; m_indexB = m_bodyB->m_islandIndex; m_localCenterA = m_bodyA->m_sweep.localCenter; m_localCenterB = m_bodyB->m_sweep.localCenter; m_invMassA = m_bodyA->m_invMass; m_invMassB = m_bodyB->m_invMass; m_invIA = m_bodyA->m_invI; m_invIB = m_bodyB->m_invI; float32 aA = data.positions[m_indexA].a; b2Vec2 vA = data.velocities[m_indexA].v; float32 wA = data.velocities[m_indexA].w; float32 aB = data.positions[m_indexB].a; b2Vec2 vB = data.velocities[m_indexB].v; float32 wB = data.velocities[m_indexB].w; b2Rot qA(aA), qB(aB); // Compute the effective mass matrix. m_rA = b2Mul(qA, m_localAnchorA - m_localCenterA); m_rB = b2Mul(qB, m_localAnchorB - m_localCenterB); // J = [-I -r1_skew I r2_skew] // [ 0 -1 0 1] // r_skew = [-ry; rx] // Matlab // K = [ mA+r1y^2*iA+mB+r2y^2*iB, -r1y*iA*r1x-r2y*iB*r2x, -r1y*iA-r2y*iB] // [ -r1y*iA*r1x-r2y*iB*r2x, mA+r1x^2*iA+mB+r2x^2*iB, r1x*iA+r2x*iB] // [ -r1y*iA-r2y*iB, r1x*iA+r2x*iB, iA+iB] float32 mA = m_invMassA, mB = m_invMassB; float32 iA = m_invIA, iB = m_invIB; b2Mat22 K; K.ex.x = mA + mB + iA * m_rA.y * m_rA.y + iB * m_rB.y * m_rB.y; K.ex.y = -iA * m_rA.x * m_rA.y - iB * m_rB.x * m_rB.y; K.ey.x = K.ex.y; K.ey.y = mA + mB + iA * m_rA.x * m_rA.x + iB * m_rB.x * m_rB.x; m_linearMass = K.GetInverse(); m_angularMass = iA + iB; if (m_angularMass > 0.0f) { m_angularMass = 1.0f / m_angularMass; } if (data.step.warmStarting) { // Scale impulses to support a variable time step. m_linearImpulse *= data.step.dtRatio; m_angularImpulse *= data.step.dtRatio; b2Vec2 P(m_linearImpulse.x, m_linearImpulse.y); vA -= mA * P; wA -= iA * (b2Cross(m_rA, P) + m_angularImpulse); vB += mB * P; wB += iB * (b2Cross(m_rB, P) + m_angularImpulse); } else { m_linearImpulse.SetZero(); m_angularImpulse = 0.0f; } data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; } void b2FrictionJoint::SolveVelocityConstraints(const b2SolverData& data) { b2Vec2 vA = data.velocities[m_indexA].v; float32 wA = data.velocities[m_indexA].w; b2Vec2 vB = data.velocities[m_indexB].v; float32 wB = data.velocities[m_indexB].w; float32 mA = m_invMassA, mB = m_invMassB; float32 iA = m_invIA, iB = m_invIB; float32 h = data.step.dt; // Solve angular friction { float32 Cdot = wB - wA; float32 impulse = -m_angularMass * Cdot; float32 oldImpulse = m_angularImpulse; float32 maxImpulse = h * m_maxTorque; m_angularImpulse = b2Clamp(m_angularImpulse + impulse, -maxImpulse, maxImpulse); impulse = m_angularImpulse - oldImpulse; wA -= iA * impulse; wB += iB * impulse; } // Solve linear friction { b2Vec2 Cdot = vB + b2Cross(wB, m_rB) - vA - b2Cross(wA, m_rA); b2Vec2 impulse = -b2Mul(m_linearMass, Cdot); b2Vec2 oldImpulse = m_linearImpulse; m_linearImpulse += impulse; float32 maxImpulse = h * m_maxForce; if (m_linearImpulse.LengthSquared() > maxImpulse * maxImpulse) { m_linearImpulse.Normalize(); m_linearImpulse *= maxImpulse; } impulse = m_linearImpulse - oldImpulse; vA -= mA * impulse; wA -= iA * b2Cross(m_rA, impulse); vB += mB * impulse; wB += iB * b2Cross(m_rB, impulse); } data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; } bool b2FrictionJoint::SolvePositionConstraints(const b2SolverData& data) { B2_NOT_USED(data); return true; } b2Vec2 b2FrictionJoint::GetAnchorA() const { return m_bodyA->GetWorldPoint(m_localAnchorA); } b2Vec2 b2FrictionJoint::GetAnchorB() const { return m_bodyB->GetWorldPoint(m_localAnchorB); } b2Vec2 b2FrictionJoint::GetReactionForce(float32 inv_dt) const { return inv_dt * m_linearImpulse; } float32 b2FrictionJoint::GetReactionTorque(float32 inv_dt) const { return inv_dt * m_angularImpulse; } void b2FrictionJoint::SetMaxForce(float32 force) { b2Assert(b2IsValid(force) && force >= 0.0f); m_maxForce = force; } float32 b2FrictionJoint::GetMaxForce() const { return m_maxForce; } void b2FrictionJoint::SetMaxTorque(float32 torque) { b2Assert(b2IsValid(torque) && torque >= 0.0f); m_maxTorque = torque; } float32 b2FrictionJoint::GetMaxTorque() const { return m_maxTorque; } void b2FrictionJoint::Dump() { int32 indexA = m_bodyA->m_islandIndex; int32 indexB = m_bodyB->m_islandIndex; b2Log(" b2FrictionJointDef jd;\n"); b2Log(" jd.bodyA = bodies[%d];\n", indexA); b2Log(" jd.bodyB = bodies[%d];\n", indexB); b2Log(" jd.collideConnected = bool(%d);\n", m_collideConnected); b2Log(" jd.localAnchorA.Set(%.15lef, %.15lef);\n", m_localAnchorA.x, m_localAnchorA.y); b2Log(" jd.localAnchorB.Set(%.15lef, %.15lef);\n", m_localAnchorB.x, m_localAnchorB.y); b2Log(" jd.maxForce = %.15lef;\n", m_maxForce); b2Log(" jd.maxTorque = %.15lef;\n", m_maxTorque); b2Log(" joints[%d] = m_world->CreateJoint(&jd);\n", m_index); } ================================================ FILE: app/src/main/cpp/b2GearJoint.cpp ================================================ /* * Copyright (c) 2007-2011 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2GearJoint.h" #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2RevoluteJoint.h" #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2PrismaticJoint.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Body.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2TimeStep.h" // Gear Joint: // C0 = (coordinate1 + ratio * coordinate2)_initial // C = (coordinate1 + ratio * coordinate2) - C0 = 0 // J = [J1 ratio * J2] // K = J * invM * JT // = J1 * invM1 * J1T + ratio * ratio * J2 * invM2 * J2T // // Revolute: // coordinate = rotation // Cdot = angularVelocity // J = [0 0 1] // K = J * invM * JT = invI // // Prismatic: // coordinate = dot(p - pg, ug) // Cdot = dot(v + cross(w, r), ug) // J = [ug cross(r, ug)] // K = J * invM * JT = invMass + invI * cross(r, ug)^2 b2GearJoint::b2GearJoint(const b2GearJointDef* def) : b2Joint(def) { m_joint1 = def->joint1; m_joint2 = def->joint2; m_typeA = m_joint1->GetType(); m_typeB = m_joint2->GetType(); b2Assert(m_typeA == e_revoluteJoint || m_typeA == e_prismaticJoint); b2Assert(m_typeB == e_revoluteJoint || m_typeB == e_prismaticJoint); float32 coordinateA, coordinateB; // TODO_ERIN there might be some problem with the joint edges in b2Joint. m_bodyC = m_joint1->GetBodyA(); m_bodyA = m_joint1->GetBodyB(); // Get geometry of joint1 b2Transform xfA = m_bodyA->m_xf; float32 aA = m_bodyA->m_sweep.a; b2Transform xfC = m_bodyC->m_xf; float32 aC = m_bodyC->m_sweep.a; if (m_typeA == e_revoluteJoint) { b2RevoluteJoint* revolute = (b2RevoluteJoint*)def->joint1; m_localAnchorC = revolute->m_localAnchorA; m_localAnchorA = revolute->m_localAnchorB; m_referenceAngleA = revolute->m_referenceAngle; m_localAxisC.SetZero(); coordinateA = aA - aC - m_referenceAngleA; } else { b2PrismaticJoint* prismatic = (b2PrismaticJoint*)def->joint1; m_localAnchorC = prismatic->m_localAnchorA; m_localAnchorA = prismatic->m_localAnchorB; m_referenceAngleA = prismatic->m_referenceAngle; m_localAxisC = prismatic->m_localXAxisA; b2Vec2 pC = m_localAnchorC; b2Vec2 pA = b2MulT(xfC.q, b2Mul(xfA.q, m_localAnchorA) + (xfA.p - xfC.p)); coordinateA = b2Dot(pA - pC, m_localAxisC); } m_bodyD = m_joint2->GetBodyA(); m_bodyB = m_joint2->GetBodyB(); // Get geometry of joint2 b2Transform xfB = m_bodyB->m_xf; float32 aB = m_bodyB->m_sweep.a; b2Transform xfD = m_bodyD->m_xf; float32 aD = m_bodyD->m_sweep.a; if (m_typeB == e_revoluteJoint) { b2RevoluteJoint* revolute = (b2RevoluteJoint*)def->joint2; m_localAnchorD = revolute->m_localAnchorA; m_localAnchorB = revolute->m_localAnchorB; m_referenceAngleB = revolute->m_referenceAngle; m_localAxisD.SetZero(); coordinateB = aB - aD - m_referenceAngleB; } else { b2PrismaticJoint* prismatic = (b2PrismaticJoint*)def->joint2; m_localAnchorD = prismatic->m_localAnchorA; m_localAnchorB = prismatic->m_localAnchorB; m_referenceAngleB = prismatic->m_referenceAngle; m_localAxisD = prismatic->m_localXAxisA; b2Vec2 pD = m_localAnchorD; b2Vec2 pB = b2MulT(xfD.q, b2Mul(xfB.q, m_localAnchorB) + (xfB.p - xfD.p)); coordinateB = b2Dot(pB - pD, m_localAxisD); } m_ratio = def->ratio; m_constant = coordinateA + m_ratio * coordinateB; m_impulse = 0.0f; } void b2GearJoint::InitVelocityConstraints(const b2SolverData& data) { m_indexA = m_bodyA->m_islandIndex; m_indexB = m_bodyB->m_islandIndex; m_indexC = m_bodyC->m_islandIndex; m_indexD = m_bodyD->m_islandIndex; m_lcA = m_bodyA->m_sweep.localCenter; m_lcB = m_bodyB->m_sweep.localCenter; m_lcC = m_bodyC->m_sweep.localCenter; m_lcD = m_bodyD->m_sweep.localCenter; m_mA = m_bodyA->m_invMass; m_mB = m_bodyB->m_invMass; m_mC = m_bodyC->m_invMass; m_mD = m_bodyD->m_invMass; m_iA = m_bodyA->m_invI; m_iB = m_bodyB->m_invI; m_iC = m_bodyC->m_invI; m_iD = m_bodyD->m_invI; float32 aA = data.positions[m_indexA].a; b2Vec2 vA = data.velocities[m_indexA].v; float32 wA = data.velocities[m_indexA].w; float32 aB = data.positions[m_indexB].a; b2Vec2 vB = data.velocities[m_indexB].v; float32 wB = data.velocities[m_indexB].w; float32 aC = data.positions[m_indexC].a; b2Vec2 vC = data.velocities[m_indexC].v; float32 wC = data.velocities[m_indexC].w; float32 aD = data.positions[m_indexD].a; b2Vec2 vD = data.velocities[m_indexD].v; float32 wD = data.velocities[m_indexD].w; b2Rot qA(aA), qB(aB), qC(aC), qD(aD); m_mass = 0.0f; if (m_typeA == e_revoluteJoint) { m_JvAC.SetZero(); m_JwA = 1.0f; m_JwC = 1.0f; m_mass += m_iA + m_iC; } else { b2Vec2 u = b2Mul(qC, m_localAxisC); b2Vec2 rC = b2Mul(qC, m_localAnchorC - m_lcC); b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_lcA); m_JvAC = u; m_JwC = b2Cross(rC, u); m_JwA = b2Cross(rA, u); m_mass += m_mC + m_mA + m_iC * m_JwC * m_JwC + m_iA * m_JwA * m_JwA; } if (m_typeB == e_revoluteJoint) { m_JvBD.SetZero(); m_JwB = m_ratio; m_JwD = m_ratio; m_mass += m_ratio * m_ratio * (m_iB + m_iD); } else { b2Vec2 u = b2Mul(qD, m_localAxisD); b2Vec2 rD = b2Mul(qD, m_localAnchorD - m_lcD); b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_lcB); m_JvBD = m_ratio * u; m_JwD = m_ratio * b2Cross(rD, u); m_JwB = m_ratio * b2Cross(rB, u); m_mass += m_ratio * m_ratio * (m_mD + m_mB) + m_iD * m_JwD * m_JwD + m_iB * m_JwB * m_JwB; } // Compute effective mass. m_mass = m_mass > 0.0f ? 1.0f / m_mass : 0.0f; if (data.step.warmStarting) { vA += (m_mA * m_impulse) * m_JvAC; wA += m_iA * m_impulse * m_JwA; vB += (m_mB * m_impulse) * m_JvBD; wB += m_iB * m_impulse * m_JwB; vC -= (m_mC * m_impulse) * m_JvAC; wC -= m_iC * m_impulse * m_JwC; vD -= (m_mD * m_impulse) * m_JvBD; wD -= m_iD * m_impulse * m_JwD; } else { m_impulse = 0.0f; } data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; data.velocities[m_indexC].v = vC; data.velocities[m_indexC].w = wC; data.velocities[m_indexD].v = vD; data.velocities[m_indexD].w = wD; } void b2GearJoint::SolveVelocityConstraints(const b2SolverData& data) { b2Vec2 vA = data.velocities[m_indexA].v; float32 wA = data.velocities[m_indexA].w; b2Vec2 vB = data.velocities[m_indexB].v; float32 wB = data.velocities[m_indexB].w; b2Vec2 vC = data.velocities[m_indexC].v; float32 wC = data.velocities[m_indexC].w; b2Vec2 vD = data.velocities[m_indexD].v; float32 wD = data.velocities[m_indexD].w; float32 Cdot = b2Dot(m_JvAC, vA - vC) + b2Dot(m_JvBD, vB - vD); Cdot += (m_JwA * wA - m_JwC * wC) + (m_JwB * wB - m_JwD * wD); float32 impulse = -m_mass * Cdot; m_impulse += impulse; vA += (m_mA * impulse) * m_JvAC; wA += m_iA * impulse * m_JwA; vB += (m_mB * impulse) * m_JvBD; wB += m_iB * impulse * m_JwB; vC -= (m_mC * impulse) * m_JvAC; wC -= m_iC * impulse * m_JwC; vD -= (m_mD * impulse) * m_JvBD; wD -= m_iD * impulse * m_JwD; data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; data.velocities[m_indexC].v = vC; data.velocities[m_indexC].w = wC; data.velocities[m_indexD].v = vD; data.velocities[m_indexD].w = wD; } bool b2GearJoint::SolvePositionConstraints(const b2SolverData& data) { b2Vec2 cA = data.positions[m_indexA].c; float32 aA = data.positions[m_indexA].a; b2Vec2 cB = data.positions[m_indexB].c; float32 aB = data.positions[m_indexB].a; b2Vec2 cC = data.positions[m_indexC].c; float32 aC = data.positions[m_indexC].a; b2Vec2 cD = data.positions[m_indexD].c; float32 aD = data.positions[m_indexD].a; b2Rot qA(aA), qB(aB), qC(aC), qD(aD); float32 linearError = 0.0f; float32 coordinateA, coordinateB; b2Vec2 JvAC, JvBD; float32 JwA, JwB, JwC, JwD; float32 mass = 0.0f; if (m_typeA == e_revoluteJoint) { JvAC.SetZero(); JwA = 1.0f; JwC = 1.0f; mass += m_iA + m_iC; coordinateA = aA - aC - m_referenceAngleA; } else { b2Vec2 u = b2Mul(qC, m_localAxisC); b2Vec2 rC = b2Mul(qC, m_localAnchorC - m_lcC); b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_lcA); JvAC = u; JwC = b2Cross(rC, u); JwA = b2Cross(rA, u); mass += m_mC + m_mA + m_iC * JwC * JwC + m_iA * JwA * JwA; b2Vec2 pC = m_localAnchorC - m_lcC; b2Vec2 pA = b2MulT(qC, rA + (cA - cC)); coordinateA = b2Dot(pA - pC, m_localAxisC); } if (m_typeB == e_revoluteJoint) { JvBD.SetZero(); JwB = m_ratio; JwD = m_ratio; mass += m_ratio * m_ratio * (m_iB + m_iD); coordinateB = aB - aD - m_referenceAngleB; } else { b2Vec2 u = b2Mul(qD, m_localAxisD); b2Vec2 rD = b2Mul(qD, m_localAnchorD - m_lcD); b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_lcB); JvBD = m_ratio * u; JwD = m_ratio * b2Cross(rD, u); JwB = m_ratio * b2Cross(rB, u); mass += m_ratio * m_ratio * (m_mD + m_mB) + m_iD * JwD * JwD + m_iB * JwB * JwB; b2Vec2 pD = m_localAnchorD - m_lcD; b2Vec2 pB = b2MulT(qD, rB + (cB - cD)); coordinateB = b2Dot(pB - pD, m_localAxisD); } float32 C = (coordinateA + m_ratio * coordinateB) - m_constant; float32 impulse = 0.0f; if (mass > 0.0f) { impulse = -C / mass; } cA += m_mA * impulse * JvAC; aA += m_iA * impulse * JwA; cB += m_mB * impulse * JvBD; aB += m_iB * impulse * JwB; cC -= m_mC * impulse * JvAC; aC -= m_iC * impulse * JwC; cD -= m_mD * impulse * JvBD; aD -= m_iD * impulse * JwD; data.positions[m_indexA].c = cA; data.positions[m_indexA].a = aA; data.positions[m_indexB].c = cB; data.positions[m_indexB].a = aB; data.positions[m_indexC].c = cC; data.positions[m_indexC].a = aC; data.positions[m_indexD].c = cD; data.positions[m_indexD].a = aD; // TODO_ERIN not implemented return linearError < b2_linearSlop; } b2Vec2 b2GearJoint::GetAnchorA() const { return m_bodyA->GetWorldPoint(m_localAnchorA); } b2Vec2 b2GearJoint::GetAnchorB() const { return m_bodyB->GetWorldPoint(m_localAnchorB); } b2Vec2 b2GearJoint::GetReactionForce(float32 inv_dt) const { b2Vec2 P = m_impulse * m_JvAC; return inv_dt * P; } float32 b2GearJoint::GetReactionTorque(float32 inv_dt) const { float32 L = m_impulse * m_JwA; return inv_dt * L; } void b2GearJoint::SetRatio(float32 ratio) { b2Assert(b2IsValid(ratio)); m_ratio = ratio; } float32 b2GearJoint::GetRatio() const { return m_ratio; } void b2GearJoint::Dump() { int32 indexA = m_bodyA->m_islandIndex; int32 indexB = m_bodyB->m_islandIndex; int32 index1 = m_joint1->m_index; int32 index2 = m_joint2->m_index; b2Log(" b2GearJointDef jd;\n"); b2Log(" jd.bodyA = bodies[%d];\n", indexA); b2Log(" jd.bodyB = bodies[%d];\n", indexB); b2Log(" jd.collideConnected = bool(%d);\n", m_collideConnected); b2Log(" jd.joint1 = joints[%d];\n", index1); b2Log(" jd.joint2 = joints[%d];\n", index2); b2Log(" jd.ratio = %.15lef;\n", m_ratio); b2Log(" joints[%d] = m_world->CreateJoint(&jd);\n", m_index); } ================================================ FILE: app/src/main/cpp/b2Island.cpp ================================================ /* * Copyright (c) 2006-2011 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Collision/b2Distance.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Island.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Body.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Fixture.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2World.h" #include "isEngine/ext_lib/Box2D/Dynamics/Contacts/b2Contact.h" #include "isEngine/ext_lib/Box2D/Dynamics/Contacts/b2ContactSolver.h" #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2Joint.h" #include "isEngine/ext_lib/Box2D/Common/b2StackAllocator.h" #include "isEngine/ext_lib/Box2D/Common/b2Timer.h" /* Position Correction Notes ========================= I tried the several algorithms for position correction of the 2D revolute joint. I looked at these systems: - simple pendulum (1m diameter sphere on massless 5m stick) with initial angular velocity of 100 rad/s. - suspension bridge with 30 1m long planks of length 1m. - multi-link chain with 30 1m long links. Here are the algorithms: Baumgarte - A fraction of the position error is added to the velocity error. There is no separate position solver. Pseudo Velocities - After the velocity solver and position integration, the position error, Jacobian, and effective mass are recomputed. Then the velocity constraints are solved with pseudo velocities and a fraction of the position error is added to the pseudo velocity error. The pseudo velocities are initialized to zero and there is no warm-starting. After the position solver, the pseudo velocities are added to the positions. This is also called the First Order World method or the Position LCP method. Modified Nonlinear Gauss-Seidel (NGS) - Like Pseudo Velocities except the position error is re-computed for each constraint and the positions are updated after the constraint is solved. The radius vectors (aka Jacobians) are re-computed too (otherwise the algorithm has horrible instability). The pseudo velocity states are not needed because they are effectively zero at the beginning of each iteration. Since we have the current position error, we allow the iterations to terminate early if the error becomes smaller than b2_linearSlop. Full NGS or just NGS - Like Modified NGS except the effective mass are re-computed each time a constraint is solved. Here are the results: Baumgarte - this is the cheapest algorithm but it has some stability problems, especially with the bridge. The chain links separate easily close to the root and they jitter as they struggle to pull together. This is one of the most common methods in the field. The big drawback is that the position correction artificially affects the momentum, thus leading to instabilities and false bounce. I used a bias factor of 0.2. A larger bias factor makes the bridge less stable, a smaller factor makes joints and contacts more spongy. Pseudo Velocities - the is more stable than the Baumgarte method. The bridge is stable. However, joints still separate with large angular velocities. Drag the simple pendulum in a circle quickly and the joint will separate. The chain separates easily and does not recover. I used a bias factor of 0.2. A larger value lead to the bridge collapsing when a heavy cube drops on it. Modified NGS - this algorithm is better in some ways than Baumgarte and Pseudo Velocities, but in other ways it is worse. The bridge and chain are much more stable, but the simple pendulum goes unstable at high angular velocities. Full NGS - stable in all tests. The joints display good stiffness. The bridge still sags, but this is better than infinite forces. Recommendations Pseudo Velocities are not really worthwhile because the bridge and chain cannot recover from joint separation. In other cases the benefit over Baumgarte is small. Modified NGS is not a robust method for the revolute joint due to the violent instability seen in the simple pendulum. Perhaps it is viable with other constraint types, especially scalar constraints where the effective mass is a scalar. This leaves Baumgarte and Full NGS. Baumgarte has small, but manageable instabilities and is very fast. I don't think we can escape Baumgarte, especially in highly demanding cases where high constraint fidelity is not needed. Full NGS is robust and easy on the eyes. I recommend this as an option for higher fidelity simulation and certainly for suspension bridges and long chains. Full NGS might be a good choice for ragdolls, especially motorized ragdolls where joint separation can be problematic. The number of NGS iterations can be reduced for better performance without harming robustness much. Each joint in a can be handled differently in the position solver. So I recommend a system where the user can select the algorithm on a per joint basis. I would probably default to the slower Full NGS and let the user select the faster Baumgarte method in performance critical scenarios. */ /* Cache Performance The Box2D solvers are dominated by cache misses. Data structures are designed to increase the number of cache hits. Much of misses are due to random access to body data. The constraint structures are iterated over linearly, which leads to few cache misses. The bodies are not accessed during iteration. Instead read only data, such as the mass values are stored with the constraints. The mutable data are the constraint impulses and the bodies velocities/positions. The impulses are held inside the constraint structures. The body velocities/positions are held in compact, temporary arrays to increase the number of cache hits. Linear and angular velocity are stored in a single array since multiple arrays lead to multiple misses. */ /* 2D Rotation R = [cos(theta) -sin(theta)] [sin(theta) cos(theta) ] thetaDot = omega Let q1 = cos(theta), q2 = sin(theta). R = [q1 -q2] [q2 q1] q1Dot = -thetaDot * q2 q2Dot = thetaDot * q1 q1_new = q1_old - dt * w * q2 q2_new = q2_old + dt * w * q1 then normalize. This might be faster than computing sin+cos. However, we can compute sin+cos of the same angle fast. */ b2Island::b2Island( int32 bodyCapacity, int32 contactCapacity, int32 jointCapacity, b2StackAllocator* allocator, b2ContactListener* listener) { m_bodyCapacity = bodyCapacity; m_contactCapacity = contactCapacity; m_jointCapacity = jointCapacity; m_bodyCount = 0; m_contactCount = 0; m_jointCount = 0; m_allocator = allocator; m_listener = listener; m_bodies = (b2Body**)m_allocator->Allocate(bodyCapacity * sizeof(b2Body*)); m_contacts = (b2Contact**)m_allocator->Allocate(contactCapacity * sizeof(b2Contact*)); m_joints = (b2Joint**)m_allocator->Allocate(jointCapacity * sizeof(b2Joint*)); m_velocities = (b2Velocity*)m_allocator->Allocate(m_bodyCapacity * sizeof(b2Velocity)); m_positions = (b2Position*)m_allocator->Allocate(m_bodyCapacity * sizeof(b2Position)); } b2Island::~b2Island() { // Warning: the order should reverse the constructor order. m_allocator->Free(m_positions); m_allocator->Free(m_velocities); m_allocator->Free(m_joints); m_allocator->Free(m_contacts); m_allocator->Free(m_bodies); } void b2Island::Solve(b2Profile* profile, const b2TimeStep& step, const b2Vec2& gravity, bool allowSleep) { b2Timer timer; float32 h = step.dt; // Integrate velocities and apply damping. Initialize the body state. for (int32 i = 0; i < m_bodyCount; ++i) { b2Body* b = m_bodies[i]; b2Vec2 c = b->m_sweep.c; float32 a = b->m_sweep.a; b2Vec2 v = b->m_linearVelocity; float32 w = b->m_angularVelocity; // Store positions for continuous collision. b->m_sweep.c0 = b->m_sweep.c; b->m_sweep.a0 = b->m_sweep.a; if (b->m_type == b2_dynamicBody) { // Integrate velocities. v += h * (b->m_gravityScale * gravity + b->m_invMass * b->m_force); w += h * b->m_invI * b->m_torque; // Apply damping. // ODE: dv/dt + c * v = 0 // Solution: v(t) = v0 * exp(-c * t) // Time step: v(t + dt) = v0 * exp(-c * (t + dt)) = v0 * exp(-c * t) * exp(-c * dt) = v * exp(-c * dt) // v2 = exp(-c * dt) * v1 // Pade approximation: // v2 = v1 * 1 / (1 + c * dt) v *= 1.0f / (1.0f + h * b->m_linearDamping); w *= 1.0f / (1.0f + h * b->m_angularDamping); } m_positions[i].c = c; m_positions[i].a = a; m_velocities[i].v = v; m_velocities[i].w = w; } timer.Reset(); // Solver data b2SolverData solverData; solverData.step = step; solverData.positions = m_positions; solverData.velocities = m_velocities; // Initialize velocity constraints. b2ContactSolverDef contactSolverDef; contactSolverDef.step = step; contactSolverDef.contacts = m_contacts; contactSolverDef.count = m_contactCount; contactSolverDef.positions = m_positions; contactSolverDef.velocities = m_velocities; contactSolverDef.allocator = m_allocator; b2ContactSolver contactSolver(&contactSolverDef); contactSolver.InitializeVelocityConstraints(); if (step.warmStarting) { contactSolver.WarmStart(); } for (int32 i = 0; i < m_jointCount; ++i) { m_joints[i]->InitVelocityConstraints(solverData); } profile->solveInit = timer.GetMilliseconds(); // Solve velocity constraints timer.Reset(); for (int32 i = 0; i < step.velocityIterations; ++i) { for (int32 j = 0; j < m_jointCount; ++j) { m_joints[j]->SolveVelocityConstraints(solverData); } contactSolver.SolveVelocityConstraints(); } // Store impulses for warm starting contactSolver.StoreImpulses(); profile->solveVelocity = timer.GetMilliseconds(); // Integrate positions for (int32 i = 0; i < m_bodyCount; ++i) { b2Vec2 c = m_positions[i].c; float32 a = m_positions[i].a; b2Vec2 v = m_velocities[i].v; float32 w = m_velocities[i].w; // Check for large velocities b2Vec2 translation = h * v; if (b2Dot(translation, translation) > b2_maxTranslationSquared) { float32 ratio = b2_maxTranslation / translation.Length(); v *= ratio; } float32 rotation = h * w; if (rotation * rotation > b2_maxRotationSquared) { float32 ratio = b2_maxRotation / b2Abs(rotation); w *= ratio; } // Integrate c += h * v; a += h * w; m_positions[i].c = c; m_positions[i].a = a; m_velocities[i].v = v; m_velocities[i].w = w; } // Solve position constraints timer.Reset(); bool positionSolved = false; for (int32 i = 0; i < step.positionIterations; ++i) { bool contactsOkay = contactSolver.SolvePositionConstraints(); bool jointsOkay = true; for (int32 i = 0; i < m_jointCount; ++i) { bool jointOkay = m_joints[i]->SolvePositionConstraints(solverData); jointsOkay = jointsOkay && jointOkay; } if (contactsOkay && jointsOkay) { // Exit early if the position errors are small. positionSolved = true; break; } } // Copy state buffers back to the bodies for (int32 i = 0; i < m_bodyCount; ++i) { b2Body* body = m_bodies[i]; body->m_sweep.c = m_positions[i].c; body->m_sweep.a = m_positions[i].a; body->m_linearVelocity = m_velocities[i].v; body->m_angularVelocity = m_velocities[i].w; body->SynchronizeTransform(); } profile->solvePosition = timer.GetMilliseconds(); Report(contactSolver.m_velocityConstraints); if (allowSleep) { float32 minSleepTime = b2_maxFloat; const float32 linTolSqr = b2_linearSleepTolerance * b2_linearSleepTolerance; const float32 angTolSqr = b2_angularSleepTolerance * b2_angularSleepTolerance; for (int32 i = 0; i < m_bodyCount; ++i) { b2Body* b = m_bodies[i]; if (b->GetType() == b2_staticBody) { continue; } if ((b->m_flags & b2Body::e_autoSleepFlag) == 0 || b->m_angularVelocity * b->m_angularVelocity > angTolSqr || b2Dot(b->m_linearVelocity, b->m_linearVelocity) > linTolSqr) { b->m_sleepTime = 0.0f; minSleepTime = 0.0f; } else { b->m_sleepTime += h; minSleepTime = b2Min(minSleepTime, b->m_sleepTime); } } if (minSleepTime >= b2_timeToSleep && positionSolved) { for (int32 i = 0; i < m_bodyCount; ++i) { b2Body* b = m_bodies[i]; b->SetAwake(false); } } } } void b2Island::SolveTOI(const b2TimeStep& subStep, int32 toiIndexA, int32 toiIndexB) { b2Assert(toiIndexA < m_bodyCount); b2Assert(toiIndexB < m_bodyCount); // Initialize the body state. for (int32 i = 0; i < m_bodyCount; ++i) { b2Body* b = m_bodies[i]; m_positions[i].c = b->m_sweep.c; m_positions[i].a = b->m_sweep.a; m_velocities[i].v = b->m_linearVelocity; m_velocities[i].w = b->m_angularVelocity; } b2ContactSolverDef contactSolverDef; contactSolverDef.contacts = m_contacts; contactSolverDef.count = m_contactCount; contactSolverDef.allocator = m_allocator; contactSolverDef.step = subStep; contactSolverDef.positions = m_positions; contactSolverDef.velocities = m_velocities; b2ContactSolver contactSolver(&contactSolverDef); // Solve position constraints. for (int32 i = 0; i < subStep.positionIterations; ++i) { bool contactsOkay = contactSolver.SolveTOIPositionConstraints(toiIndexA, toiIndexB); if (contactsOkay) { break; } } #if 0 // Is the new position really safe? for (int32 i = 0; i < m_contactCount; ++i) { b2Contact* c = m_contacts[i]; b2Fixture* fA = c->GetFixtureA(); b2Fixture* fB = c->GetFixtureB(); b2Body* bA = fA->GetBody(); b2Body* bB = fB->GetBody(); int32 indexA = c->GetChildIndexA(); int32 indexB = c->GetChildIndexB(); b2DistanceInput input; input.proxyA.Set(fA->GetShape(), indexA); input.proxyB.Set(fB->GetShape(), indexB); input.transformA = bA->GetTransform(); input.transformB = bB->GetTransform(); input.useRadii = false; b2DistanceOutput output; b2SimplexCache cache; cache.count = 0; b2Distance(&output, &cache, &input); if (output.distance == 0 || cache.count == 3) { cache.count += 0; } } #endif // Leap of faith to new safe state. m_bodies[toiIndexA]->m_sweep.c0 = m_positions[toiIndexA].c; m_bodies[toiIndexA]->m_sweep.a0 = m_positions[toiIndexA].a; m_bodies[toiIndexB]->m_sweep.c0 = m_positions[toiIndexB].c; m_bodies[toiIndexB]->m_sweep.a0 = m_positions[toiIndexB].a; // No warm starting is needed for TOI events because warm // starting impulses were applied in the discrete solver. contactSolver.InitializeVelocityConstraints(); // Solve velocity constraints. for (int32 i = 0; i < subStep.velocityIterations; ++i) { contactSolver.SolveVelocityConstraints(); } // Don't store the TOI contact forces for warm starting // because they can be quite large. float32 h = subStep.dt; // Integrate positions for (int32 i = 0; i < m_bodyCount; ++i) { b2Vec2 c = m_positions[i].c; float32 a = m_positions[i].a; b2Vec2 v = m_velocities[i].v; float32 w = m_velocities[i].w; // Check for large velocities b2Vec2 translation = h * v; if (b2Dot(translation, translation) > b2_maxTranslationSquared) { float32 ratio = b2_maxTranslation / translation.Length(); v *= ratio; } float32 rotation = h * w; if (rotation * rotation > b2_maxRotationSquared) { float32 ratio = b2_maxRotation / b2Abs(rotation); w *= ratio; } // Integrate c += h * v; a += h * w; m_positions[i].c = c; m_positions[i].a = a; m_velocities[i].v = v; m_velocities[i].w = w; // Sync bodies b2Body* body = m_bodies[i]; body->m_sweep.c = c; body->m_sweep.a = a; body->m_linearVelocity = v; body->m_angularVelocity = w; body->SynchronizeTransform(); } Report(contactSolver.m_velocityConstraints); } void b2Island::Report(const b2ContactVelocityConstraint* constraints) { if (m_listener == NULL) { return; } for (int32 i = 0; i < m_contactCount; ++i) { b2Contact* c = m_contacts[i]; const b2ContactVelocityConstraint* vc = constraints + i; b2ContactImpulse impulse; impulse.count = vc->pointCount; for (int32 j = 0; j < vc->pointCount; ++j) { impulse.normalImpulses[j] = vc->points[j].normalImpulse; impulse.tangentImpulses[j] = vc->points[j].tangentImpulse; } m_listener->PostSolve(c, &impulse); } } ================================================ FILE: app/src/main/cpp/b2Joint.cpp ================================================ /* * Copyright (c) 2006-2007 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2Joint.h" #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2DistanceJoint.h" #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2WheelJoint.h" #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2MouseJoint.h" #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2RevoluteJoint.h" #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2PrismaticJoint.h" #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2PulleyJoint.h" #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2GearJoint.h" #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2WeldJoint.h" #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2FrictionJoint.h" #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2RopeJoint.h" #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2MotorJoint.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Body.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2World.h" #include "isEngine/ext_lib/Box2D/Common/b2BlockAllocator.h" #include b2Joint* b2Joint::Create(const b2JointDef* def, b2BlockAllocator* allocator) { b2Joint* joint = NULL; switch (def->type) { case e_distanceJoint: { void* mem = allocator->Allocate(sizeof(b2DistanceJoint)); joint = new (mem) b2DistanceJoint(static_cast(def)); } break; case e_mouseJoint: { void* mem = allocator->Allocate(sizeof(b2MouseJoint)); joint = new (mem) b2MouseJoint(static_cast(def)); } break; case e_prismaticJoint: { void* mem = allocator->Allocate(sizeof(b2PrismaticJoint)); joint = new (mem) b2PrismaticJoint(static_cast(def)); } break; case e_revoluteJoint: { void* mem = allocator->Allocate(sizeof(b2RevoluteJoint)); joint = new (mem) b2RevoluteJoint(static_cast(def)); } break; case e_pulleyJoint: { void* mem = allocator->Allocate(sizeof(b2PulleyJoint)); joint = new (mem) b2PulleyJoint(static_cast(def)); } break; case e_gearJoint: { void* mem = allocator->Allocate(sizeof(b2GearJoint)); joint = new (mem) b2GearJoint(static_cast(def)); } break; case e_wheelJoint: { void* mem = allocator->Allocate(sizeof(b2WheelJoint)); joint = new (mem) b2WheelJoint(static_cast(def)); } break; case e_weldJoint: { void* mem = allocator->Allocate(sizeof(b2WeldJoint)); joint = new (mem) b2WeldJoint(static_cast(def)); } break; case e_frictionJoint: { void* mem = allocator->Allocate(sizeof(b2FrictionJoint)); joint = new (mem) b2FrictionJoint(static_cast(def)); } break; case e_ropeJoint: { void* mem = allocator->Allocate(sizeof(b2RopeJoint)); joint = new (mem) b2RopeJoint(static_cast(def)); } break; case e_motorJoint: { void* mem = allocator->Allocate(sizeof(b2MotorJoint)); joint = new (mem) b2MotorJoint(static_cast(def)); } break; default: b2Assert(false); break; } return joint; } void b2Joint::Destroy(b2Joint* joint, b2BlockAllocator* allocator) { joint->~b2Joint(); switch (joint->m_type) { case e_distanceJoint: allocator->Free(joint, sizeof(b2DistanceJoint)); break; case e_mouseJoint: allocator->Free(joint, sizeof(b2MouseJoint)); break; case e_prismaticJoint: allocator->Free(joint, sizeof(b2PrismaticJoint)); break; case e_revoluteJoint: allocator->Free(joint, sizeof(b2RevoluteJoint)); break; case e_pulleyJoint: allocator->Free(joint, sizeof(b2PulleyJoint)); break; case e_gearJoint: allocator->Free(joint, sizeof(b2GearJoint)); break; case e_wheelJoint: allocator->Free(joint, sizeof(b2WheelJoint)); break; case e_weldJoint: allocator->Free(joint, sizeof(b2WeldJoint)); break; case e_frictionJoint: allocator->Free(joint, sizeof(b2FrictionJoint)); break; case e_ropeJoint: allocator->Free(joint, sizeof(b2RopeJoint)); break; case e_motorJoint: allocator->Free(joint, sizeof(b2MotorJoint)); break; default: b2Assert(false); break; } } b2Joint::b2Joint(const b2JointDef* def) { b2Assert(def->bodyA != def->bodyB); m_type = def->type; m_prev = NULL; m_next = NULL; m_bodyA = def->bodyA; m_bodyB = def->bodyB; m_index = 0; m_collideConnected = def->collideConnected; m_islandFlag = false; m_userData = def->userData; m_edgeA.joint = NULL; m_edgeA.other = NULL; m_edgeA.prev = NULL; m_edgeA.next = NULL; m_edgeB.joint = NULL; m_edgeB.other = NULL; m_edgeB.prev = NULL; m_edgeB.next = NULL; } bool b2Joint::IsActive() const { return m_bodyA->IsActive() && m_bodyB->IsActive(); } ================================================ FILE: app/src/main/cpp/b2Math.cpp ================================================ /* * Copyright (c) 2007-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Common/b2Math.h" const b2Vec2 b2Vec2_zero(0.0f, 0.0f); /// Solve A * x = b, where b is a column vector. This is more efficient /// than computing the inverse in one-shot cases. b2Vec3 b2Mat33::Solve33(const b2Vec3& b) const { float32 det = b2Dot(ex, b2Cross(ey, ez)); if (det != 0.0f) { det = 1.0f / det; } b2Vec3 x; x.x = det * b2Dot(b, b2Cross(ey, ez)); x.y = det * b2Dot(ex, b2Cross(b, ez)); x.z = det * b2Dot(ex, b2Cross(ey, b)); return x; } /// Solve A * x = b, where b is a column vector. This is more efficient /// than computing the inverse in one-shot cases. b2Vec2 b2Mat33::Solve22(const b2Vec2& b) const { float32 a11 = ex.x, a12 = ey.x, a21 = ex.y, a22 = ey.y; float32 det = a11 * a22 - a12 * a21; if (det != 0.0f) { det = 1.0f / det; } b2Vec2 x; x.x = det * (a22 * b.x - a12 * b.y); x.y = det * (a11 * b.y - a21 * b.x); return x; } /// void b2Mat33::GetInverse22(b2Mat33* M) const { float32 a = ex.x, b = ey.x, c = ex.y, d = ey.y; float32 det = a * d - b * c; if (det != 0.0f) { det = 1.0f / det; } M->ex.x = det * d; M->ey.x = -det * b; M->ex.z = 0.0f; M->ex.y = -det * c; M->ey.y = det * a; M->ey.z = 0.0f; M->ez.x = 0.0f; M->ez.y = 0.0f; M->ez.z = 0.0f; } /// Returns the zero matrix if singular. void b2Mat33::GetSymInverse33(b2Mat33* M) const { float32 det = b2Dot(ex, b2Cross(ey, ez)); if (det != 0.0f) { det = 1.0f / det; } float32 a11 = ex.x, a12 = ey.x, a13 = ez.x; float32 a22 = ey.y, a23 = ez.y; float32 a33 = ez.z; M->ex.x = det * (a22 * a33 - a23 * a23); M->ex.y = det * (a13 * a23 - a12 * a33); M->ex.z = det * (a12 * a23 - a13 * a22); M->ey.x = M->ex.y; M->ey.y = det * (a11 * a33 - a13 * a13); M->ey.z = det * (a13 * a12 - a11 * a23); M->ez.x = M->ex.z; M->ez.y = M->ey.z; M->ez.z = det * (a11 * a22 - a12 * a12); } ================================================ FILE: app/src/main/cpp/b2MotorJoint.cpp ================================================ /* * Copyright (c) 2006-2012 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2MotorJoint.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Body.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2TimeStep.h" // Point-to-point constraint // Cdot = v2 - v1 // = v2 + cross(w2, r2) - v1 - cross(w1, r1) // J = [-I -r1_skew I r2_skew ] // Identity used: // w k % (rx i + ry j) = w * (-ry i + rx j) // Angle constraint // Cdot = w2 - w1 // J = [0 0 -1 0 0 1] // K = invI1 + invI2 void b2MotorJointDef::Initialize(b2Body* bA, b2Body* bB) { bodyA = bA; bodyB = bB; b2Vec2 xB = bodyB->GetPosition(); linearOffset = bodyA->GetLocalPoint(xB); float32 angleA = bodyA->GetAngle(); float32 angleB = bodyB->GetAngle(); angularOffset = angleB - angleA; } b2MotorJoint::b2MotorJoint(const b2MotorJointDef* def) : b2Joint(def) { m_linearOffset = def->linearOffset; m_angularOffset = def->angularOffset; m_linearImpulse.SetZero(); m_angularImpulse = 0.0f; m_maxForce = def->maxForce; m_maxTorque = def->maxTorque; m_correctionFactor = def->correctionFactor; } void b2MotorJoint::InitVelocityConstraints(const b2SolverData& data) { m_indexA = m_bodyA->m_islandIndex; m_indexB = m_bodyB->m_islandIndex; m_localCenterA = m_bodyA->m_sweep.localCenter; m_localCenterB = m_bodyB->m_sweep.localCenter; m_invMassA = m_bodyA->m_invMass; m_invMassB = m_bodyB->m_invMass; m_invIA = m_bodyA->m_invI; m_invIB = m_bodyB->m_invI; b2Vec2 cA = data.positions[m_indexA].c; float32 aA = data.positions[m_indexA].a; b2Vec2 vA = data.velocities[m_indexA].v; float32 wA = data.velocities[m_indexA].w; b2Vec2 cB = data.positions[m_indexB].c; float32 aB = data.positions[m_indexB].a; b2Vec2 vB = data.velocities[m_indexB].v; float32 wB = data.velocities[m_indexB].w; b2Rot qA(aA), qB(aB); // Compute the effective mass matrix. m_rA = b2Mul(qA, -m_localCenterA); m_rB = b2Mul(qB, -m_localCenterB); // J = [-I -r1_skew I r2_skew] // [ 0 -1 0 1] // r_skew = [-ry; rx] // Matlab // K = [ mA+r1y^2*iA+mB+r2y^2*iB, -r1y*iA*r1x-r2y*iB*r2x, -r1y*iA-r2y*iB] // [ -r1y*iA*r1x-r2y*iB*r2x, mA+r1x^2*iA+mB+r2x^2*iB, r1x*iA+r2x*iB] // [ -r1y*iA-r2y*iB, r1x*iA+r2x*iB, iA+iB] float32 mA = m_invMassA, mB = m_invMassB; float32 iA = m_invIA, iB = m_invIB; b2Mat22 K; K.ex.x = mA + mB + iA * m_rA.y * m_rA.y + iB * m_rB.y * m_rB.y; K.ex.y = -iA * m_rA.x * m_rA.y - iB * m_rB.x * m_rB.y; K.ey.x = K.ex.y; K.ey.y = mA + mB + iA * m_rA.x * m_rA.x + iB * m_rB.x * m_rB.x; m_linearMass = K.GetInverse(); m_angularMass = iA + iB; if (m_angularMass > 0.0f) { m_angularMass = 1.0f / m_angularMass; } m_linearError = cB + m_rB - cA - m_rA - b2Mul(qA, m_linearOffset); m_angularError = aB - aA - m_angularOffset; if (data.step.warmStarting) { // Scale impulses to support a variable time step. m_linearImpulse *= data.step.dtRatio; m_angularImpulse *= data.step.dtRatio; b2Vec2 P(m_linearImpulse.x, m_linearImpulse.y); vA -= mA * P; wA -= iA * (b2Cross(m_rA, P) + m_angularImpulse); vB += mB * P; wB += iB * (b2Cross(m_rB, P) + m_angularImpulse); } else { m_linearImpulse.SetZero(); m_angularImpulse = 0.0f; } data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; } void b2MotorJoint::SolveVelocityConstraints(const b2SolverData& data) { b2Vec2 vA = data.velocities[m_indexA].v; float32 wA = data.velocities[m_indexA].w; b2Vec2 vB = data.velocities[m_indexB].v; float32 wB = data.velocities[m_indexB].w; float32 mA = m_invMassA, mB = m_invMassB; float32 iA = m_invIA, iB = m_invIB; float32 h = data.step.dt; float32 inv_h = data.step.inv_dt; // Solve angular friction { float32 Cdot = wB - wA + inv_h * m_correctionFactor * m_angularError; float32 impulse = -m_angularMass * Cdot; float32 oldImpulse = m_angularImpulse; float32 maxImpulse = h * m_maxTorque; m_angularImpulse = b2Clamp(m_angularImpulse + impulse, -maxImpulse, maxImpulse); impulse = m_angularImpulse - oldImpulse; wA -= iA * impulse; wB += iB * impulse; } // Solve linear friction { b2Vec2 Cdot = vB + b2Cross(wB, m_rB) - vA - b2Cross(wA, m_rA) + inv_h * m_correctionFactor * m_linearError; b2Vec2 impulse = -b2Mul(m_linearMass, Cdot); b2Vec2 oldImpulse = m_linearImpulse; m_linearImpulse += impulse; float32 maxImpulse = h * m_maxForce; if (m_linearImpulse.LengthSquared() > maxImpulse * maxImpulse) { m_linearImpulse.Normalize(); m_linearImpulse *= maxImpulse; } impulse = m_linearImpulse - oldImpulse; vA -= mA * impulse; wA -= iA * b2Cross(m_rA, impulse); vB += mB * impulse; wB += iB * b2Cross(m_rB, impulse); } data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; } bool b2MotorJoint::SolvePositionConstraints(const b2SolverData& data) { B2_NOT_USED(data); return true; } b2Vec2 b2MotorJoint::GetAnchorA() const { return m_bodyA->GetPosition(); } b2Vec2 b2MotorJoint::GetAnchorB() const { return m_bodyB->GetPosition(); } b2Vec2 b2MotorJoint::GetReactionForce(float32 inv_dt) const { return inv_dt * m_linearImpulse; } float32 b2MotorJoint::GetReactionTorque(float32 inv_dt) const { return inv_dt * m_angularImpulse; } void b2MotorJoint::SetMaxForce(float32 force) { b2Assert(b2IsValid(force) && force >= 0.0f); m_maxForce = force; } float32 b2MotorJoint::GetMaxForce() const { return m_maxForce; } void b2MotorJoint::SetMaxTorque(float32 torque) { b2Assert(b2IsValid(torque) && torque >= 0.0f); m_maxTorque = torque; } float32 b2MotorJoint::GetMaxTorque() const { return m_maxTorque; } void b2MotorJoint::SetCorrectionFactor(float32 factor) { b2Assert(b2IsValid(factor) && 0.0f <= factor && factor <= 1.0f); m_correctionFactor = factor; } float32 b2MotorJoint::GetCorrectionFactor() const { return m_correctionFactor; } void b2MotorJoint::SetLinearOffset(const b2Vec2& linearOffset) { if (linearOffset.x != m_linearOffset.x || linearOffset.y != m_linearOffset.y) { m_bodyA->SetAwake(true); m_bodyB->SetAwake(true); m_linearOffset = linearOffset; } } const b2Vec2& b2MotorJoint::GetLinearOffset() const { return m_linearOffset; } void b2MotorJoint::SetAngularOffset(float32 angularOffset) { if (angularOffset != m_angularOffset) { m_bodyA->SetAwake(true); m_bodyB->SetAwake(true); m_angularOffset = angularOffset; } } float32 b2MotorJoint::GetAngularOffset() const { return m_angularOffset; } void b2MotorJoint::Dump() { int32 indexA = m_bodyA->m_islandIndex; int32 indexB = m_bodyB->m_islandIndex; b2Log(" b2MotorJointDef jd;\n"); b2Log(" jd.bodyA = bodies[%d];\n", indexA); b2Log(" jd.bodyB = bodies[%d];\n", indexB); b2Log(" jd.collideConnected = bool(%d);\n", m_collideConnected); b2Log(" jd.linearOffset.Set(%.15lef, %.15lef);\n", m_linearOffset.x, m_linearOffset.y); b2Log(" jd.angularOffset = %.15lef;\n", m_angularOffset); b2Log(" jd.maxForce = %.15lef;\n", m_maxForce); b2Log(" jd.maxTorque = %.15lef;\n", m_maxTorque); b2Log(" jd.correctionFactor = %.15lef;\n", m_correctionFactor); b2Log(" joints[%d] = m_world->CreateJoint(&jd);\n", m_index); } ================================================ FILE: app/src/main/cpp/b2MouseJoint.cpp ================================================ /* * Copyright (c) 2006-2007 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2MouseJoint.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Body.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2TimeStep.h" // p = attached point, m = mouse point // C = p - m // Cdot = v // = v + cross(w, r) // J = [I r_skew] // Identity used: // w k % (rx i + ry j) = w * (-ry i + rx j) b2MouseJoint::b2MouseJoint(const b2MouseJointDef* def) : b2Joint(def) { b2Assert(def->target.IsValid()); b2Assert(b2IsValid(def->maxForce) && def->maxForce >= 0.0f); b2Assert(b2IsValid(def->frequencyHz) && def->frequencyHz >= 0.0f); b2Assert(b2IsValid(def->dampingRatio) && def->dampingRatio >= 0.0f); m_targetA = def->target; m_localAnchorB = b2MulT(m_bodyB->GetTransform(), m_targetA); m_maxForce = def->maxForce; m_impulse.SetZero(); m_frequencyHz = def->frequencyHz; m_dampingRatio = def->dampingRatio; m_beta = 0.0f; m_gamma = 0.0f; } void b2MouseJoint::SetTarget(const b2Vec2& target) { if (m_bodyB->IsAwake() == false) { m_bodyB->SetAwake(true); } m_targetA = target; } const b2Vec2& b2MouseJoint::GetTarget() const { return m_targetA; } void b2MouseJoint::SetMaxForce(float32 force) { m_maxForce = force; } float32 b2MouseJoint::GetMaxForce() const { return m_maxForce; } void b2MouseJoint::SetFrequency(float32 hz) { m_frequencyHz = hz; } float32 b2MouseJoint::GetFrequency() const { return m_frequencyHz; } void b2MouseJoint::SetDampingRatio(float32 ratio) { m_dampingRatio = ratio; } float32 b2MouseJoint::GetDampingRatio() const { return m_dampingRatio; } void b2MouseJoint::InitVelocityConstraints(const b2SolverData& data) { m_indexB = m_bodyB->m_islandIndex; m_localCenterB = m_bodyB->m_sweep.localCenter; m_invMassB = m_bodyB->m_invMass; m_invIB = m_bodyB->m_invI; b2Vec2 cB = data.positions[m_indexB].c; float32 aB = data.positions[m_indexB].a; b2Vec2 vB = data.velocities[m_indexB].v; float32 wB = data.velocities[m_indexB].w; b2Rot qB(aB); float32 mass = m_bodyB->GetMass(); // Frequency float32 omega = 2.0f * b2_pi * m_frequencyHz; // Damping coefficient float32 d = 2.0f * mass * m_dampingRatio * omega; // Spring stiffness float32 k = mass * (omega * omega); // magic formulas // gamma has units of inverse mass. // beta has units of inverse time. float32 h = data.step.dt; b2Assert(d + h * k > b2_epsilon); m_gamma = h * (d + h * k); if (m_gamma != 0.0f) { m_gamma = 1.0f / m_gamma; } m_beta = h * k * m_gamma; // Compute the effective mass matrix. m_rB = b2Mul(qB, m_localAnchorB - m_localCenterB); // K = [(1/m1 + 1/m2) * eye(2) - skew(r1) * invI1 * skew(r1) - skew(r2) * invI2 * skew(r2)] // = [1/m1+1/m2 0 ] + invI1 * [r1.y*r1.y -r1.x*r1.y] + invI2 * [r1.y*r1.y -r1.x*r1.y] // [ 0 1/m1+1/m2] [-r1.x*r1.y r1.x*r1.x] [-r1.x*r1.y r1.x*r1.x] b2Mat22 K; K.ex.x = m_invMassB + m_invIB * m_rB.y * m_rB.y + m_gamma; K.ex.y = -m_invIB * m_rB.x * m_rB.y; K.ey.x = K.ex.y; K.ey.y = m_invMassB + m_invIB * m_rB.x * m_rB.x + m_gamma; m_mass = K.GetInverse(); m_C = cB + m_rB - m_targetA; m_C *= m_beta; // Cheat with some damping wB *= 0.98f; if (data.step.warmStarting) { m_impulse *= data.step.dtRatio; vB += m_invMassB * m_impulse; wB += m_invIB * b2Cross(m_rB, m_impulse); } else { m_impulse.SetZero(); } data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; } void b2MouseJoint::SolveVelocityConstraints(const b2SolverData& data) { b2Vec2 vB = data.velocities[m_indexB].v; float32 wB = data.velocities[m_indexB].w; // Cdot = v + cross(w, r) b2Vec2 Cdot = vB + b2Cross(wB, m_rB); b2Vec2 impulse = b2Mul(m_mass, -(Cdot + m_C + m_gamma * m_impulse)); b2Vec2 oldImpulse = m_impulse; m_impulse += impulse; float32 maxImpulse = data.step.dt * m_maxForce; if (m_impulse.LengthSquared() > maxImpulse * maxImpulse) { m_impulse *= maxImpulse / m_impulse.Length(); } impulse = m_impulse - oldImpulse; vB += m_invMassB * impulse; wB += m_invIB * b2Cross(m_rB, impulse); data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; } bool b2MouseJoint::SolvePositionConstraints(const b2SolverData& data) { B2_NOT_USED(data); return true; } b2Vec2 b2MouseJoint::GetAnchorA() const { return m_targetA; } b2Vec2 b2MouseJoint::GetAnchorB() const { return m_bodyB->GetWorldPoint(m_localAnchorB); } b2Vec2 b2MouseJoint::GetReactionForce(float32 inv_dt) const { return inv_dt * m_impulse; } float32 b2MouseJoint::GetReactionTorque(float32 inv_dt) const { return inv_dt * 0.0f; } void b2MouseJoint::ShiftOrigin(const b2Vec2& newOrigin) { m_targetA -= newOrigin; } ================================================ FILE: app/src/main/cpp/b2PolygonAndCircleContact.cpp ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Dynamics/Contacts/b2PolygonAndCircleContact.h" #include "isEngine/ext_lib/Box2D/Common/b2BlockAllocator.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Fixture.h" #include b2Contact* b2PolygonAndCircleContact::Create(b2Fixture* fixtureA, int32, b2Fixture* fixtureB, int32, b2BlockAllocator* allocator) { void* mem = allocator->Allocate(sizeof(b2PolygonAndCircleContact)); return new (mem) b2PolygonAndCircleContact(fixtureA, fixtureB); } void b2PolygonAndCircleContact::Destroy(b2Contact* contact, b2BlockAllocator* allocator) { ((b2PolygonAndCircleContact*)contact)->~b2PolygonAndCircleContact(); allocator->Free(contact, sizeof(b2PolygonAndCircleContact)); } b2PolygonAndCircleContact::b2PolygonAndCircleContact(b2Fixture* fixtureA, b2Fixture* fixtureB) : b2Contact(fixtureA, 0, fixtureB, 0) { b2Assert(m_fixtureA->GetType() == b2Shape::e_polygon); b2Assert(m_fixtureB->GetType() == b2Shape::e_circle); } void b2PolygonAndCircleContact::Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB) { b2CollidePolygonAndCircle( manifold, (b2PolygonShape*)m_fixtureA->GetShape(), xfA, (b2CircleShape*)m_fixtureB->GetShape(), xfB); } ================================================ FILE: app/src/main/cpp/b2PolygonContact.cpp ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Dynamics/Contacts/b2PolygonContact.h" #include "isEngine/ext_lib/Box2D/Common/b2BlockAllocator.h" #include "isEngine/ext_lib/Box2D/Collision/b2TimeOfImpact.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Body.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Fixture.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2WorldCallbacks.h" #include b2Contact* b2PolygonContact::Create(b2Fixture* fixtureA, int32, b2Fixture* fixtureB, int32, b2BlockAllocator* allocator) { void* mem = allocator->Allocate(sizeof(b2PolygonContact)); return new (mem) b2PolygonContact(fixtureA, fixtureB); } void b2PolygonContact::Destroy(b2Contact* contact, b2BlockAllocator* allocator) { ((b2PolygonContact*)contact)->~b2PolygonContact(); allocator->Free(contact, sizeof(b2PolygonContact)); } b2PolygonContact::b2PolygonContact(b2Fixture* fixtureA, b2Fixture* fixtureB) : b2Contact(fixtureA, 0, fixtureB, 0) { b2Assert(m_fixtureA->GetType() == b2Shape::e_polygon); b2Assert(m_fixtureB->GetType() == b2Shape::e_polygon); } void b2PolygonContact::Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB) { b2CollidePolygons( manifold, (b2PolygonShape*)m_fixtureA->GetShape(), xfA, (b2PolygonShape*)m_fixtureB->GetShape(), xfB); } ================================================ FILE: app/src/main/cpp/b2PolygonShape.cpp ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2PolygonShape.h" #include b2Shape* b2PolygonShape::Clone(b2BlockAllocator* allocator) const { void* mem = allocator->Allocate(sizeof(b2PolygonShape)); b2PolygonShape* clone = new (mem) b2PolygonShape; *clone = *this; return clone; } void b2PolygonShape::SetAsBox(float32 hx, float32 hy) { m_count = 4; m_vertices[0].Set(-hx, -hy); m_vertices[1].Set( hx, -hy); m_vertices[2].Set( hx, hy); m_vertices[3].Set(-hx, hy); m_normals[0].Set(0.0f, -1.0f); m_normals[1].Set(1.0f, 0.0f); m_normals[2].Set(0.0f, 1.0f); m_normals[3].Set(-1.0f, 0.0f); m_centroid.SetZero(); } void b2PolygonShape::SetAsBox(float32 hx, float32 hy, const b2Vec2& center, float32 angle) { m_count = 4; m_vertices[0].Set(-hx, -hy); m_vertices[1].Set( hx, -hy); m_vertices[2].Set( hx, hy); m_vertices[3].Set(-hx, hy); m_normals[0].Set(0.0f, -1.0f); m_normals[1].Set(1.0f, 0.0f); m_normals[2].Set(0.0f, 1.0f); m_normals[3].Set(-1.0f, 0.0f); m_centroid = center; b2Transform xf; xf.p = center; xf.q.Set(angle); // Transform vertices and normals. for (int32 i = 0; i < m_count; ++i) { m_vertices[i] = b2Mul(xf, m_vertices[i]); m_normals[i] = b2Mul(xf.q, m_normals[i]); } } int32 b2PolygonShape::GetChildCount() const { return 1; } static b2Vec2 ComputeCentroid(const b2Vec2* vs, int32 count) { b2Assert(count >= 3); b2Vec2 c; c.Set(0.0f, 0.0f); float32 area = 0.0f; // pRef is the reference point for forming triangles. // It's location doesn't change the result (except for rounding error). b2Vec2 pRef(0.0f, 0.0f); #if 0 // This code would put the reference point inside the polygon. for (int32 i = 0; i < count; ++i) { pRef += vs[i]; } pRef *= 1.0f / count; #endif const float32 inv3 = 1.0f / 3.0f; for (int32 i = 0; i < count; ++i) { // Triangle vertices. b2Vec2 p1 = pRef; b2Vec2 p2 = vs[i]; b2Vec2 p3 = i + 1 < count ? vs[i+1] : vs[0]; b2Vec2 e1 = p2 - p1; b2Vec2 e2 = p3 - p1; float32 D = b2Cross(e1, e2); float32 triangleArea = 0.5f * D; area += triangleArea; // Area weighted centroid c += triangleArea * inv3 * (p1 + p2 + p3); } // Centroid b2Assert(area > b2_epsilon); c *= 1.0f / area; return c; } void b2PolygonShape::Set(const b2Vec2* vertices, int32 count) { b2Assert(3 <= count && count <= b2_maxPolygonVertices); if (count < 3) { SetAsBox(1.0f, 1.0f); return; } int32 n = b2Min(count, b2_maxPolygonVertices); // Perform welding and copy vertices into local buffer. b2Vec2 ps[b2_maxPolygonVertices]; int32 tempCount = 0; for (int32 i = 0; i < n; ++i) { b2Vec2 v = vertices[i]; bool unique = true; for (int32 j = 0; j < tempCount; ++j) { if (b2DistanceSquared(v, ps[j]) < 0.5f * b2_linearSlop) { unique = false; break; } } if (unique) { ps[tempCount++] = v; } } n = tempCount; if (n < 3) { // Polygon is degenerate. b2Assert(false); SetAsBox(1.0f, 1.0f); return; } // Create the convex hull using the Gift wrapping algorithm // http://en.wikipedia.org/wiki/Gift_wrapping_algorithm // Find the right most point on the hull int32 i0 = 0; float32 x0 = ps[0].x; for (int32 i = 1; i < n; ++i) { float32 x = ps[i].x; if (x > x0 || (x == x0 && ps[i].y < ps[i0].y)) { i0 = i; x0 = x; } } int32 hull[b2_maxPolygonVertices]; int32 m = 0; int32 ih = i0; for (;;) { hull[m] = ih; int32 ie = 0; for (int32 j = 1; j < n; ++j) { if (ie == ih) { ie = j; continue; } b2Vec2 r = ps[ie] - ps[hull[m]]; b2Vec2 v = ps[j] - ps[hull[m]]; float32 c = b2Cross(r, v); if (c < 0.0f) { ie = j; } // Collinearity check if (c == 0.0f && v.LengthSquared() > r.LengthSquared()) { ie = j; } } ++m; ih = ie; if (ie == i0) { break; } } m_count = m; // Copy vertices. for (int32 i = 0; i < m; ++i) { m_vertices[i] = ps[hull[i]]; } // Compute normals. Ensure the edges have non-zero length. for (int32 i = 0; i < m; ++i) { int32 i1 = i; int32 i2 = i + 1 < m ? i + 1 : 0; b2Vec2 edge = m_vertices[i2] - m_vertices[i1]; b2Assert(edge.LengthSquared() > b2_epsilon * b2_epsilon); m_normals[i] = b2Cross(edge, 1.0f); m_normals[i].Normalize(); } // Compute the polygon centroid. m_centroid = ComputeCentroid(m_vertices, m); } bool b2PolygonShape::TestPoint(const b2Transform& xf, const b2Vec2& p) const { b2Vec2 pLocal = b2MulT(xf.q, p - xf.p); for (int32 i = 0; i < m_count; ++i) { float32 dot = b2Dot(m_normals[i], pLocal - m_vertices[i]); if (dot > 0.0f) { return false; } } return true; } bool b2PolygonShape::RayCast(b2RayCastOutput* output, const b2RayCastInput& input, const b2Transform& xf, int32 childIndex) const { B2_NOT_USED(childIndex); // Put the ray into the polygon's frame of reference. b2Vec2 p1 = b2MulT(xf.q, input.p1 - xf.p); b2Vec2 p2 = b2MulT(xf.q, input.p2 - xf.p); b2Vec2 d = p2 - p1; float32 lower = 0.0f, upper = input.maxFraction; int32 index = -1; for (int32 i = 0; i < m_count; ++i) { // p = p1 + a * d // dot(normal, p - v) = 0 // dot(normal, p1 - v) + a * dot(normal, d) = 0 float32 numerator = b2Dot(m_normals[i], m_vertices[i] - p1); float32 denominator = b2Dot(m_normals[i], d); if (denominator == 0.0f) { if (numerator < 0.0f) { return false; } } else { // Note: we want this predicate without division: // lower < numerator / denominator, where denominator < 0 // Since denominator < 0, we have to flip the inequality: // lower < numerator / denominator <==> denominator * lower > numerator. if (denominator < 0.0f && numerator < lower * denominator) { // Increase lower. // The segment enters this half-space. lower = numerator / denominator; index = i; } else if (denominator > 0.0f && numerator < upper * denominator) { // Decrease upper. // The segment exits this half-space. upper = numerator / denominator; } } // The use of epsilon here causes the assert on lower to trip // in some cases. Apparently the use of epsilon was to make edge // shapes work, but now those are handled separately. //if (upper < lower - b2_epsilon) if (upper < lower) { return false; } } b2Assert(0.0f <= lower && lower <= input.maxFraction); if (index >= 0) { output->fraction = lower; output->normal = b2Mul(xf.q, m_normals[index]); return true; } return false; } void b2PolygonShape::ComputeAABB(b2AABB* aabb, const b2Transform& xf, int32 childIndex) const { B2_NOT_USED(childIndex); b2Vec2 lower = b2Mul(xf, m_vertices[0]); b2Vec2 upper = lower; for (int32 i = 1; i < m_count; ++i) { b2Vec2 v = b2Mul(xf, m_vertices[i]); lower = b2Min(lower, v); upper = b2Max(upper, v); } b2Vec2 r(m_radius, m_radius); aabb->lowerBound = lower - r; aabb->upperBound = upper + r; } void b2PolygonShape::ComputeMass(b2MassData* massData, float32 density) const { // Polygon mass, centroid, and inertia. // Let rho be the polygon density in mass per unit area. // Then: // mass = rho * int(dA) // centroid.x = (1/mass) * rho * int(x * dA) // centroid.y = (1/mass) * rho * int(y * dA) // I = rho * int((x*x + y*y) * dA) // // We can compute these integrals by summing all the integrals // for each triangle of the polygon. To evaluate the integral // for a single triangle, we make a change of variables to // the (u,v) coordinates of the triangle: // x = x0 + e1x * u + e2x * v // y = y0 + e1y * u + e2y * v // where 0 <= u && 0 <= v && u + v <= 1. // // We integrate u from [0,1-v] and then v from [0,1]. // We also need to use the Jacobian of the transformation: // D = cross(e1, e2) // // Simplification: triangle centroid = (1/3) * (p1 + p2 + p3) // // The rest of the derivation is handled by computer algebra. b2Assert(m_count >= 3); b2Vec2 center; center.Set(0.0f, 0.0f); float32 area = 0.0f; float32 I = 0.0f; // s is the reference point for forming triangles. // It's location doesn't change the result (except for rounding error). b2Vec2 s(0.0f, 0.0f); // This code would put the reference point inside the polygon. for (int32 i = 0; i < m_count; ++i) { s += m_vertices[i]; } s *= 1.0f / m_count; const float32 k_inv3 = 1.0f / 3.0f; for (int32 i = 0; i < m_count; ++i) { // Triangle vertices. b2Vec2 e1 = m_vertices[i] - s; b2Vec2 e2 = i + 1 < m_count ? m_vertices[i+1] - s : m_vertices[0] - s; float32 D = b2Cross(e1, e2); float32 triangleArea = 0.5f * D; area += triangleArea; // Area weighted centroid center += triangleArea * k_inv3 * (e1 + e2); float32 ex1 = e1.x, ey1 = e1.y; float32 ex2 = e2.x, ey2 = e2.y; float32 intx2 = ex1*ex1 + ex2*ex1 + ex2*ex2; float32 inty2 = ey1*ey1 + ey2*ey1 + ey2*ey2; I += (0.25f * k_inv3 * D) * (intx2 + inty2); } // Total mass massData->mass = density * area; // Center of mass b2Assert(area > b2_epsilon); center *= 1.0f / area; massData->center = center + s; // Inertia tensor relative to the local origin (point s). massData->I = density * I; // Shift to center of mass then to original body origin. massData->I += massData->mass * (b2Dot(massData->center, massData->center) - b2Dot(center, center)); } bool b2PolygonShape::Validate() const { for (int32 i = 0; i < m_count; ++i) { int32 i1 = i; int32 i2 = i < m_count - 1 ? i1 + 1 : 0; b2Vec2 p = m_vertices[i1]; b2Vec2 e = m_vertices[i2] - p; for (int32 j = 0; j < m_count; ++j) { if (j == i1 || j == i2) { continue; } b2Vec2 v = m_vertices[j] - p; float32 c = b2Cross(e, v); if (c < 0.0f) { return false; } } } return true; } ================================================ FILE: app/src/main/cpp/b2PrismaticJoint.cpp ================================================ /* * Copyright (c) 2006-2011 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2PrismaticJoint.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Body.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2TimeStep.h" // Linear constraint (point-to-line) // d = p2 - p1 = x2 + r2 - x1 - r1 // C = dot(perp, d) // Cdot = dot(d, cross(w1, perp)) + dot(perp, v2 + cross(w2, r2) - v1 - cross(w1, r1)) // = -dot(perp, v1) - dot(cross(d + r1, perp), w1) + dot(perp, v2) + dot(cross(r2, perp), v2) // J = [-perp, -cross(d + r1, perp), perp, cross(r2,perp)] // // Angular constraint // C = a2 - a1 + a_initial // Cdot = w2 - w1 // J = [0 0 -1 0 0 1] // // K = J * invM * JT // // J = [-a -s1 a s2] // [0 -1 0 1] // a = perp // s1 = cross(d + r1, a) = cross(p2 - x1, a) // s2 = cross(r2, a) = cross(p2 - x2, a) // Motor/Limit linear constraint // C = dot(ax1, d) // Cdot = = -dot(ax1, v1) - dot(cross(d + r1, ax1), w1) + dot(ax1, v2) + dot(cross(r2, ax1), v2) // J = [-ax1 -cross(d+r1,ax1) ax1 cross(r2,ax1)] // Block Solver // We develop a block solver that includes the joint limit. This makes the limit stiff (inelastic) even // when the mass has poor distribution (leading to large torques about the joint anchor points). // // The Jacobian has 3 rows: // J = [-uT -s1 uT s2] // linear // [0 -1 0 1] // angular // [-vT -a1 vT a2] // limit // // u = perp // v = axis // s1 = cross(d + r1, u), s2 = cross(r2, u) // a1 = cross(d + r1, v), a2 = cross(r2, v) // M * (v2 - v1) = JT * df // J * v2 = bias // // v2 = v1 + invM * JT * df // J * (v1 + invM * JT * df) = bias // K * df = bias - J * v1 = -Cdot // K = J * invM * JT // Cdot = J * v1 - bias // // Now solve for f2. // df = f2 - f1 // K * (f2 - f1) = -Cdot // f2 = invK * (-Cdot) + f1 // // Clamp accumulated limit impulse. // lower: f2(3) = max(f2(3), 0) // upper: f2(3) = min(f2(3), 0) // // Solve for correct f2(1:2) // K(1:2, 1:2) * f2(1:2) = -Cdot(1:2) - K(1:2,3) * f2(3) + K(1:2,1:3) * f1 // = -Cdot(1:2) - K(1:2,3) * f2(3) + K(1:2,1:2) * f1(1:2) + K(1:2,3) * f1(3) // K(1:2, 1:2) * f2(1:2) = -Cdot(1:2) - K(1:2,3) * (f2(3) - f1(3)) + K(1:2,1:2) * f1(1:2) // f2(1:2) = invK(1:2,1:2) * (-Cdot(1:2) - K(1:2,3) * (f2(3) - f1(3))) + f1(1:2) // // Now compute impulse to be applied: // df = f2 - f1 void b2PrismaticJointDef::Initialize(b2Body* bA, b2Body* bB, const b2Vec2& anchor, const b2Vec2& axis) { bodyA = bA; bodyB = bB; localAnchorA = bodyA->GetLocalPoint(anchor); localAnchorB = bodyB->GetLocalPoint(anchor); localAxisA = bodyA->GetLocalVector(axis); referenceAngle = bodyB->GetAngle() - bodyA->GetAngle(); } b2PrismaticJoint::b2PrismaticJoint(const b2PrismaticJointDef* def) : b2Joint(def) { m_localAnchorA = def->localAnchorA; m_localAnchorB = def->localAnchorB; m_localXAxisA = def->localAxisA; m_localXAxisA.Normalize(); m_localYAxisA = b2Cross(1.0f, m_localXAxisA); m_referenceAngle = def->referenceAngle; m_impulse.SetZero(); m_motorMass = 0.0f; m_motorImpulse = 0.0f; m_lowerTranslation = def->lowerTranslation; m_upperTranslation = def->upperTranslation; m_maxMotorForce = def->maxMotorForce; m_motorSpeed = def->motorSpeed; m_enableLimit = def->enableLimit; m_enableMotor = def->enableMotor; m_limitState = e_inactiveLimit; m_axis.SetZero(); m_perp.SetZero(); } void b2PrismaticJoint::InitVelocityConstraints(const b2SolverData& data) { m_indexA = m_bodyA->m_islandIndex; m_indexB = m_bodyB->m_islandIndex; m_localCenterA = m_bodyA->m_sweep.localCenter; m_localCenterB = m_bodyB->m_sweep.localCenter; m_invMassA = m_bodyA->m_invMass; m_invMassB = m_bodyB->m_invMass; m_invIA = m_bodyA->m_invI; m_invIB = m_bodyB->m_invI; b2Vec2 cA = data.positions[m_indexA].c; float32 aA = data.positions[m_indexA].a; b2Vec2 vA = data.velocities[m_indexA].v; float32 wA = data.velocities[m_indexA].w; b2Vec2 cB = data.positions[m_indexB].c; float32 aB = data.positions[m_indexB].a; b2Vec2 vB = data.velocities[m_indexB].v; float32 wB = data.velocities[m_indexB].w; b2Rot qA(aA), qB(aB); // Compute the effective masses. b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA); b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB); b2Vec2 d = (cB - cA) + rB - rA; float32 mA = m_invMassA, mB = m_invMassB; float32 iA = m_invIA, iB = m_invIB; // Compute motor Jacobian and effective mass. { m_axis = b2Mul(qA, m_localXAxisA); m_a1 = b2Cross(d + rA, m_axis); m_a2 = b2Cross(rB, m_axis); m_motorMass = mA + mB + iA * m_a1 * m_a1 + iB * m_a2 * m_a2; if (m_motorMass > 0.0f) { m_motorMass = 1.0f / m_motorMass; } } // Prismatic constraint. { m_perp = b2Mul(qA, m_localYAxisA); m_s1 = b2Cross(d + rA, m_perp); m_s2 = b2Cross(rB, m_perp); float32 k11 = mA + mB + iA * m_s1 * m_s1 + iB * m_s2 * m_s2; float32 k12 = iA * m_s1 + iB * m_s2; float32 k13 = iA * m_s1 * m_a1 + iB * m_s2 * m_a2; float32 k22 = iA + iB; if (k22 == 0.0f) { // For bodies with fixed rotation. k22 = 1.0f; } float32 k23 = iA * m_a1 + iB * m_a2; float32 k33 = mA + mB + iA * m_a1 * m_a1 + iB * m_a2 * m_a2; m_K.ex.Set(k11, k12, k13); m_K.ey.Set(k12, k22, k23); m_K.ez.Set(k13, k23, k33); } // Compute motor and limit terms. if (m_enableLimit) { float32 jointTranslation = b2Dot(m_axis, d); if (b2Abs(m_upperTranslation - m_lowerTranslation) < 2.0f * b2_linearSlop) { m_limitState = e_equalLimits; } else if (jointTranslation <= m_lowerTranslation) { if (m_limitState != e_atLowerLimit) { m_limitState = e_atLowerLimit; m_impulse.z = 0.0f; } } else if (jointTranslation >= m_upperTranslation) { if (m_limitState != e_atUpperLimit) { m_limitState = e_atUpperLimit; m_impulse.z = 0.0f; } } else { m_limitState = e_inactiveLimit; m_impulse.z = 0.0f; } } else { m_limitState = e_inactiveLimit; m_impulse.z = 0.0f; } if (m_enableMotor == false) { m_motorImpulse = 0.0f; } if (data.step.warmStarting) { // Account for variable time step. m_impulse *= data.step.dtRatio; m_motorImpulse *= data.step.dtRatio; b2Vec2 P = m_impulse.x * m_perp + (m_motorImpulse + m_impulse.z) * m_axis; float32 LA = m_impulse.x * m_s1 + m_impulse.y + (m_motorImpulse + m_impulse.z) * m_a1; float32 LB = m_impulse.x * m_s2 + m_impulse.y + (m_motorImpulse + m_impulse.z) * m_a2; vA -= mA * P; wA -= iA * LA; vB += mB * P; wB += iB * LB; } else { m_impulse.SetZero(); m_motorImpulse = 0.0f; } data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; } void b2PrismaticJoint::SolveVelocityConstraints(const b2SolverData& data) { b2Vec2 vA = data.velocities[m_indexA].v; float32 wA = data.velocities[m_indexA].w; b2Vec2 vB = data.velocities[m_indexB].v; float32 wB = data.velocities[m_indexB].w; float32 mA = m_invMassA, mB = m_invMassB; float32 iA = m_invIA, iB = m_invIB; // Solve linear motor constraint. if (m_enableMotor && m_limitState != e_equalLimits) { float32 Cdot = b2Dot(m_axis, vB - vA) + m_a2 * wB - m_a1 * wA; float32 impulse = m_motorMass * (m_motorSpeed - Cdot); float32 oldImpulse = m_motorImpulse; float32 maxImpulse = data.step.dt * m_maxMotorForce; m_motorImpulse = b2Clamp(m_motorImpulse + impulse, -maxImpulse, maxImpulse); impulse = m_motorImpulse - oldImpulse; b2Vec2 P = impulse * m_axis; float32 LA = impulse * m_a1; float32 LB = impulse * m_a2; vA -= mA * P; wA -= iA * LA; vB += mB * P; wB += iB * LB; } b2Vec2 Cdot1; Cdot1.x = b2Dot(m_perp, vB - vA) + m_s2 * wB - m_s1 * wA; Cdot1.y = wB - wA; if (m_enableLimit && m_limitState != e_inactiveLimit) { // Solve prismatic and limit constraint in block form. float32 Cdot2; Cdot2 = b2Dot(m_axis, vB - vA) + m_a2 * wB - m_a1 * wA; b2Vec3 Cdot(Cdot1.x, Cdot1.y, Cdot2); b2Vec3 f1 = m_impulse; b2Vec3 df = m_K.Solve33(-Cdot); m_impulse += df; if (m_limitState == e_atLowerLimit) { m_impulse.z = b2Max(m_impulse.z, 0.0f); } else if (m_limitState == e_atUpperLimit) { m_impulse.z = b2Min(m_impulse.z, 0.0f); } // f2(1:2) = invK(1:2,1:2) * (-Cdot(1:2) - K(1:2,3) * (f2(3) - f1(3))) + f1(1:2) b2Vec2 b = -Cdot1 - (m_impulse.z - f1.z) * b2Vec2(m_K.ez.x, m_K.ez.y); b2Vec2 f2r = m_K.Solve22(b) + b2Vec2(f1.x, f1.y); m_impulse.x = f2r.x; m_impulse.y = f2r.y; df = m_impulse - f1; b2Vec2 P = df.x * m_perp + df.z * m_axis; float32 LA = df.x * m_s1 + df.y + df.z * m_a1; float32 LB = df.x * m_s2 + df.y + df.z * m_a2; vA -= mA * P; wA -= iA * LA; vB += mB * P; wB += iB * LB; } else { // Limit is inactive, just solve the prismatic constraint in block form. b2Vec2 df = m_K.Solve22(-Cdot1); m_impulse.x += df.x; m_impulse.y += df.y; b2Vec2 P = df.x * m_perp; float32 LA = df.x * m_s1 + df.y; float32 LB = df.x * m_s2 + df.y; vA -= mA * P; wA -= iA * LA; vB += mB * P; wB += iB * LB; } data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; } bool b2PrismaticJoint::SolvePositionConstraints(const b2SolverData& data) { b2Vec2 cA = data.positions[m_indexA].c; float32 aA = data.positions[m_indexA].a; b2Vec2 cB = data.positions[m_indexB].c; float32 aB = data.positions[m_indexB].a; b2Rot qA(aA), qB(aB); float32 mA = m_invMassA, mB = m_invMassB; float32 iA = m_invIA, iB = m_invIB; // Compute fresh Jacobians b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA); b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB); b2Vec2 d = cB + rB - cA - rA; b2Vec2 axis = b2Mul(qA, m_localXAxisA); float32 a1 = b2Cross(d + rA, axis); float32 a2 = b2Cross(rB, axis); b2Vec2 perp = b2Mul(qA, m_localYAxisA); float32 s1 = b2Cross(d + rA, perp); float32 s2 = b2Cross(rB, perp); b2Vec3 impulse; b2Vec2 C1; C1.x = b2Dot(perp, d); C1.y = aB - aA - m_referenceAngle; float32 linearError = b2Abs(C1.x); float32 angularError = b2Abs(C1.y); bool active = false; float32 C2 = 0.0f; if (m_enableLimit) { float32 translation = b2Dot(axis, d); if (b2Abs(m_upperTranslation - m_lowerTranslation) < 2.0f * b2_linearSlop) { // Prevent large angular corrections C2 = b2Clamp(translation, -b2_maxLinearCorrection, b2_maxLinearCorrection); linearError = b2Max(linearError, b2Abs(translation)); active = true; } else if (translation <= m_lowerTranslation) { // Prevent large linear corrections and allow some slop. C2 = b2Clamp(translation - m_lowerTranslation + b2_linearSlop, -b2_maxLinearCorrection, 0.0f); linearError = b2Max(linearError, m_lowerTranslation - translation); active = true; } else if (translation >= m_upperTranslation) { // Prevent large linear corrections and allow some slop. C2 = b2Clamp(translation - m_upperTranslation - b2_linearSlop, 0.0f, b2_maxLinearCorrection); linearError = b2Max(linearError, translation - m_upperTranslation); active = true; } } if (active) { float32 k11 = mA + mB + iA * s1 * s1 + iB * s2 * s2; float32 k12 = iA * s1 + iB * s2; float32 k13 = iA * s1 * a1 + iB * s2 * a2; float32 k22 = iA + iB; if (k22 == 0.0f) { // For fixed rotation k22 = 1.0f; } float32 k23 = iA * a1 + iB * a2; float32 k33 = mA + mB + iA * a1 * a1 + iB * a2 * a2; b2Mat33 K; K.ex.Set(k11, k12, k13); K.ey.Set(k12, k22, k23); K.ez.Set(k13, k23, k33); b2Vec3 C; C.x = C1.x; C.y = C1.y; C.z = C2; impulse = K.Solve33(-C); } else { float32 k11 = mA + mB + iA * s1 * s1 + iB * s2 * s2; float32 k12 = iA * s1 + iB * s2; float32 k22 = iA + iB; if (k22 == 0.0f) { k22 = 1.0f; } b2Mat22 K; K.ex.Set(k11, k12); K.ey.Set(k12, k22); b2Vec2 impulse1 = K.Solve(-C1); impulse.x = impulse1.x; impulse.y = impulse1.y; impulse.z = 0.0f; } b2Vec2 P = impulse.x * perp + impulse.z * axis; float32 LA = impulse.x * s1 + impulse.y + impulse.z * a1; float32 LB = impulse.x * s2 + impulse.y + impulse.z * a2; cA -= mA * P; aA -= iA * LA; cB += mB * P; aB += iB * LB; data.positions[m_indexA].c = cA; data.positions[m_indexA].a = aA; data.positions[m_indexB].c = cB; data.positions[m_indexB].a = aB; return linearError <= b2_linearSlop && angularError <= b2_angularSlop; } b2Vec2 b2PrismaticJoint::GetAnchorA() const { return m_bodyA->GetWorldPoint(m_localAnchorA); } b2Vec2 b2PrismaticJoint::GetAnchorB() const { return m_bodyB->GetWorldPoint(m_localAnchorB); } b2Vec2 b2PrismaticJoint::GetReactionForce(float32 inv_dt) const { return inv_dt * (m_impulse.x * m_perp + (m_motorImpulse + m_impulse.z) * m_axis); } float32 b2PrismaticJoint::GetReactionTorque(float32 inv_dt) const { return inv_dt * m_impulse.y; } float32 b2PrismaticJoint::GetJointTranslation() const { b2Vec2 pA = m_bodyA->GetWorldPoint(m_localAnchorA); b2Vec2 pB = m_bodyB->GetWorldPoint(m_localAnchorB); b2Vec2 d = pB - pA; b2Vec2 axis = m_bodyA->GetWorldVector(m_localXAxisA); float32 translation = b2Dot(d, axis); return translation; } float32 b2PrismaticJoint::GetJointSpeed() const { b2Body* bA = m_bodyA; b2Body* bB = m_bodyB; b2Vec2 rA = b2Mul(bA->m_xf.q, m_localAnchorA - bA->m_sweep.localCenter); b2Vec2 rB = b2Mul(bB->m_xf.q, m_localAnchorB - bB->m_sweep.localCenter); b2Vec2 p1 = bA->m_sweep.c + rA; b2Vec2 p2 = bB->m_sweep.c + rB; b2Vec2 d = p2 - p1; b2Vec2 axis = b2Mul(bA->m_xf.q, m_localXAxisA); b2Vec2 vA = bA->m_linearVelocity; b2Vec2 vB = bB->m_linearVelocity; float32 wA = bA->m_angularVelocity; float32 wB = bB->m_angularVelocity; float32 speed = b2Dot(d, b2Cross(wA, axis)) + b2Dot(axis, vB + b2Cross(wB, rB) - vA - b2Cross(wA, rA)); return speed; } bool b2PrismaticJoint::IsLimitEnabled() const { return m_enableLimit; } void b2PrismaticJoint::EnableLimit(bool flag) { if (flag != m_enableLimit) { m_bodyA->SetAwake(true); m_bodyB->SetAwake(true); m_enableLimit = flag; m_impulse.z = 0.0f; } } float32 b2PrismaticJoint::GetLowerLimit() const { return m_lowerTranslation; } float32 b2PrismaticJoint::GetUpperLimit() const { return m_upperTranslation; } void b2PrismaticJoint::SetLimits(float32 lower, float32 upper) { b2Assert(lower <= upper); if (lower != m_lowerTranslation || upper != m_upperTranslation) { m_bodyA->SetAwake(true); m_bodyB->SetAwake(true); m_lowerTranslation = lower; m_upperTranslation = upper; m_impulse.z = 0.0f; } } bool b2PrismaticJoint::IsMotorEnabled() const { return m_enableMotor; } void b2PrismaticJoint::EnableMotor(bool flag) { m_bodyA->SetAwake(true); m_bodyB->SetAwake(true); m_enableMotor = flag; } void b2PrismaticJoint::SetMotorSpeed(float32 speed) { m_bodyA->SetAwake(true); m_bodyB->SetAwake(true); m_motorSpeed = speed; } void b2PrismaticJoint::SetMaxMotorForce(float32 force) { m_bodyA->SetAwake(true); m_bodyB->SetAwake(true); m_maxMotorForce = force; } float32 b2PrismaticJoint::GetMotorForce(float32 inv_dt) const { return inv_dt * m_motorImpulse; } void b2PrismaticJoint::Dump() { int32 indexA = m_bodyA->m_islandIndex; int32 indexB = m_bodyB->m_islandIndex; b2Log(" b2PrismaticJointDef jd;\n"); b2Log(" jd.bodyA = bodies[%d];\n", indexA); b2Log(" jd.bodyB = bodies[%d];\n", indexB); b2Log(" jd.collideConnected = bool(%d);\n", m_collideConnected); b2Log(" jd.localAnchorA.Set(%.15lef, %.15lef);\n", m_localAnchorA.x, m_localAnchorA.y); b2Log(" jd.localAnchorB.Set(%.15lef, %.15lef);\n", m_localAnchorB.x, m_localAnchorB.y); b2Log(" jd.localAxisA.Set(%.15lef, %.15lef);\n", m_localXAxisA.x, m_localXAxisA.y); b2Log(" jd.referenceAngle = %.15lef;\n", m_referenceAngle); b2Log(" jd.enableLimit = bool(%d);\n", m_enableLimit); b2Log(" jd.lowerTranslation = %.15lef;\n", m_lowerTranslation); b2Log(" jd.upperTranslation = %.15lef;\n", m_upperTranslation); b2Log(" jd.enableMotor = bool(%d);\n", m_enableMotor); b2Log(" jd.motorSpeed = %.15lef;\n", m_motorSpeed); b2Log(" jd.maxMotorForce = %.15lef;\n", m_maxMotorForce); b2Log(" joints[%d] = m_world->CreateJoint(&jd);\n", m_index); } ================================================ FILE: app/src/main/cpp/b2PulleyJoint.cpp ================================================ /* * Copyright (c) 2007 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2PulleyJoint.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Body.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2TimeStep.h" // Pulley: // length1 = norm(p1 - s1) // length2 = norm(p2 - s2) // C0 = (length1 + ratio * length2)_initial // C = C0 - (length1 + ratio * length2) // u1 = (p1 - s1) / norm(p1 - s1) // u2 = (p2 - s2) / norm(p2 - s2) // Cdot = -dot(u1, v1 + cross(w1, r1)) - ratio * dot(u2, v2 + cross(w2, r2)) // J = -[u1 cross(r1, u1) ratio * u2 ratio * cross(r2, u2)] // K = J * invM * JT // = invMass1 + invI1 * cross(r1, u1)^2 + ratio^2 * (invMass2 + invI2 * cross(r2, u2)^2) void b2PulleyJointDef::Initialize(b2Body* bA, b2Body* bB, const b2Vec2& groundA, const b2Vec2& groundB, const b2Vec2& anchorA, const b2Vec2& anchorB, float32 r) { bodyA = bA; bodyB = bB; groundAnchorA = groundA; groundAnchorB = groundB; localAnchorA = bodyA->GetLocalPoint(anchorA); localAnchorB = bodyB->GetLocalPoint(anchorB); b2Vec2 dA = anchorA - groundA; lengthA = dA.Length(); b2Vec2 dB = anchorB - groundB; lengthB = dB.Length(); ratio = r; b2Assert(ratio > b2_epsilon); } b2PulleyJoint::b2PulleyJoint(const b2PulleyJointDef* def) : b2Joint(def) { m_groundAnchorA = def->groundAnchorA; m_groundAnchorB = def->groundAnchorB; m_localAnchorA = def->localAnchorA; m_localAnchorB = def->localAnchorB; m_lengthA = def->lengthA; m_lengthB = def->lengthB; b2Assert(def->ratio != 0.0f); m_ratio = def->ratio; m_constant = def->lengthA + m_ratio * def->lengthB; m_impulse = 0.0f; } void b2PulleyJoint::InitVelocityConstraints(const b2SolverData& data) { m_indexA = m_bodyA->m_islandIndex; m_indexB = m_bodyB->m_islandIndex; m_localCenterA = m_bodyA->m_sweep.localCenter; m_localCenterB = m_bodyB->m_sweep.localCenter; m_invMassA = m_bodyA->m_invMass; m_invMassB = m_bodyB->m_invMass; m_invIA = m_bodyA->m_invI; m_invIB = m_bodyB->m_invI; b2Vec2 cA = data.positions[m_indexA].c; float32 aA = data.positions[m_indexA].a; b2Vec2 vA = data.velocities[m_indexA].v; float32 wA = data.velocities[m_indexA].w; b2Vec2 cB = data.positions[m_indexB].c; float32 aB = data.positions[m_indexB].a; b2Vec2 vB = data.velocities[m_indexB].v; float32 wB = data.velocities[m_indexB].w; b2Rot qA(aA), qB(aB); m_rA = b2Mul(qA, m_localAnchorA - m_localCenterA); m_rB = b2Mul(qB, m_localAnchorB - m_localCenterB); // Get the pulley axes. m_uA = cA + m_rA - m_groundAnchorA; m_uB = cB + m_rB - m_groundAnchorB; float32 lengthA = m_uA.Length(); float32 lengthB = m_uB.Length(); if (lengthA > 10.0f * b2_linearSlop) { m_uA *= 1.0f / lengthA; } else { m_uA.SetZero(); } if (lengthB > 10.0f * b2_linearSlop) { m_uB *= 1.0f / lengthB; } else { m_uB.SetZero(); } // Compute effective mass. float32 ruA = b2Cross(m_rA, m_uA); float32 ruB = b2Cross(m_rB, m_uB); float32 mA = m_invMassA + m_invIA * ruA * ruA; float32 mB = m_invMassB + m_invIB * ruB * ruB; m_mass = mA + m_ratio * m_ratio * mB; if (m_mass > 0.0f) { m_mass = 1.0f / m_mass; } if (data.step.warmStarting) { // Scale impulses to support variable time steps. m_impulse *= data.step.dtRatio; // Warm starting. b2Vec2 PA = -(m_impulse) * m_uA; b2Vec2 PB = (-m_ratio * m_impulse) * m_uB; vA += m_invMassA * PA; wA += m_invIA * b2Cross(m_rA, PA); vB += m_invMassB * PB; wB += m_invIB * b2Cross(m_rB, PB); } else { m_impulse = 0.0f; } data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; } void b2PulleyJoint::SolveVelocityConstraints(const b2SolverData& data) { b2Vec2 vA = data.velocities[m_indexA].v; float32 wA = data.velocities[m_indexA].w; b2Vec2 vB = data.velocities[m_indexB].v; float32 wB = data.velocities[m_indexB].w; b2Vec2 vpA = vA + b2Cross(wA, m_rA); b2Vec2 vpB = vB + b2Cross(wB, m_rB); float32 Cdot = -b2Dot(m_uA, vpA) - m_ratio * b2Dot(m_uB, vpB); float32 impulse = -m_mass * Cdot; m_impulse += impulse; b2Vec2 PA = -impulse * m_uA; b2Vec2 PB = -m_ratio * impulse * m_uB; vA += m_invMassA * PA; wA += m_invIA * b2Cross(m_rA, PA); vB += m_invMassB * PB; wB += m_invIB * b2Cross(m_rB, PB); data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; } bool b2PulleyJoint::SolvePositionConstraints(const b2SolverData& data) { b2Vec2 cA = data.positions[m_indexA].c; float32 aA = data.positions[m_indexA].a; b2Vec2 cB = data.positions[m_indexB].c; float32 aB = data.positions[m_indexB].a; b2Rot qA(aA), qB(aB); b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA); b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB); // Get the pulley axes. b2Vec2 uA = cA + rA - m_groundAnchorA; b2Vec2 uB = cB + rB - m_groundAnchorB; float32 lengthA = uA.Length(); float32 lengthB = uB.Length(); if (lengthA > 10.0f * b2_linearSlop) { uA *= 1.0f / lengthA; } else { uA.SetZero(); } if (lengthB > 10.0f * b2_linearSlop) { uB *= 1.0f / lengthB; } else { uB.SetZero(); } // Compute effective mass. float32 ruA = b2Cross(rA, uA); float32 ruB = b2Cross(rB, uB); float32 mA = m_invMassA + m_invIA * ruA * ruA; float32 mB = m_invMassB + m_invIB * ruB * ruB; float32 mass = mA + m_ratio * m_ratio * mB; if (mass > 0.0f) { mass = 1.0f / mass; } float32 C = m_constant - lengthA - m_ratio * lengthB; float32 linearError = b2Abs(C); float32 impulse = -mass * C; b2Vec2 PA = -impulse * uA; b2Vec2 PB = -m_ratio * impulse * uB; cA += m_invMassA * PA; aA += m_invIA * b2Cross(rA, PA); cB += m_invMassB * PB; aB += m_invIB * b2Cross(rB, PB); data.positions[m_indexA].c = cA; data.positions[m_indexA].a = aA; data.positions[m_indexB].c = cB; data.positions[m_indexB].a = aB; return linearError < b2_linearSlop; } b2Vec2 b2PulleyJoint::GetAnchorA() const { return m_bodyA->GetWorldPoint(m_localAnchorA); } b2Vec2 b2PulleyJoint::GetAnchorB() const { return m_bodyB->GetWorldPoint(m_localAnchorB); } b2Vec2 b2PulleyJoint::GetReactionForce(float32 inv_dt) const { b2Vec2 P = m_impulse * m_uB; return inv_dt * P; } float32 b2PulleyJoint::GetReactionTorque(float32 inv_dt) const { B2_NOT_USED(inv_dt); return 0.0f; } b2Vec2 b2PulleyJoint::GetGroundAnchorA() const { return m_groundAnchorA; } b2Vec2 b2PulleyJoint::GetGroundAnchorB() const { return m_groundAnchorB; } float32 b2PulleyJoint::GetLengthA() const { return m_lengthA; } float32 b2PulleyJoint::GetLengthB() const { return m_lengthB; } float32 b2PulleyJoint::GetRatio() const { return m_ratio; } float32 b2PulleyJoint::GetCurrentLengthA() const { b2Vec2 p = m_bodyA->GetWorldPoint(m_localAnchorA); b2Vec2 s = m_groundAnchorA; b2Vec2 d = p - s; return d.Length(); } float32 b2PulleyJoint::GetCurrentLengthB() const { b2Vec2 p = m_bodyB->GetWorldPoint(m_localAnchorB); b2Vec2 s = m_groundAnchorB; b2Vec2 d = p - s; return d.Length(); } void b2PulleyJoint::Dump() { int32 indexA = m_bodyA->m_islandIndex; int32 indexB = m_bodyB->m_islandIndex; b2Log(" b2PulleyJointDef jd;\n"); b2Log(" jd.bodyA = bodies[%d];\n", indexA); b2Log(" jd.bodyB = bodies[%d];\n", indexB); b2Log(" jd.collideConnected = bool(%d);\n", m_collideConnected); b2Log(" jd.groundAnchorA.Set(%.15lef, %.15lef);\n", m_groundAnchorA.x, m_groundAnchorA.y); b2Log(" jd.groundAnchorB.Set(%.15lef, %.15lef);\n", m_groundAnchorB.x, m_groundAnchorB.y); b2Log(" jd.localAnchorA.Set(%.15lef, %.15lef);\n", m_localAnchorA.x, m_localAnchorA.y); b2Log(" jd.localAnchorB.Set(%.15lef, %.15lef);\n", m_localAnchorB.x, m_localAnchorB.y); b2Log(" jd.lengthA = %.15lef;\n", m_lengthA); b2Log(" jd.lengthB = %.15lef;\n", m_lengthB); b2Log(" jd.ratio = %.15lef;\n", m_ratio); b2Log(" joints[%d] = m_world->CreateJoint(&jd);\n", m_index); } void b2PulleyJoint::ShiftOrigin(const b2Vec2& newOrigin) { m_groundAnchorA -= newOrigin; m_groundAnchorB -= newOrigin; } ================================================ FILE: app/src/main/cpp/b2RevoluteJoint.cpp ================================================ /* * Copyright (c) 2006-2011 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2RevoluteJoint.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Body.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2TimeStep.h" // Point-to-point constraint // C = p2 - p1 // Cdot = v2 - v1 // = v2 + cross(w2, r2) - v1 - cross(w1, r1) // J = [-I -r1_skew I r2_skew ] // Identity used: // w k % (rx i + ry j) = w * (-ry i + rx j) // Motor constraint // Cdot = w2 - w1 // J = [0 0 -1 0 0 1] // K = invI1 + invI2 void b2RevoluteJointDef::Initialize(b2Body* bA, b2Body* bB, const b2Vec2& anchor) { bodyA = bA; bodyB = bB; localAnchorA = bodyA->GetLocalPoint(anchor); localAnchorB = bodyB->GetLocalPoint(anchor); referenceAngle = bodyB->GetAngle() - bodyA->GetAngle(); } b2RevoluteJoint::b2RevoluteJoint(const b2RevoluteJointDef* def) : b2Joint(def) { m_localAnchorA = def->localAnchorA; m_localAnchorB = def->localAnchorB; m_referenceAngle = def->referenceAngle; m_impulse.SetZero(); m_motorImpulse = 0.0f; m_lowerAngle = def->lowerAngle; m_upperAngle = def->upperAngle; m_maxMotorTorque = def->maxMotorTorque; m_motorSpeed = def->motorSpeed; m_enableLimit = def->enableLimit; m_enableMotor = def->enableMotor; m_limitState = e_inactiveLimit; } void b2RevoluteJoint::InitVelocityConstraints(const b2SolverData& data) { m_indexA = m_bodyA->m_islandIndex; m_indexB = m_bodyB->m_islandIndex; m_localCenterA = m_bodyA->m_sweep.localCenter; m_localCenterB = m_bodyB->m_sweep.localCenter; m_invMassA = m_bodyA->m_invMass; m_invMassB = m_bodyB->m_invMass; m_invIA = m_bodyA->m_invI; m_invIB = m_bodyB->m_invI; float32 aA = data.positions[m_indexA].a; b2Vec2 vA = data.velocities[m_indexA].v; float32 wA = data.velocities[m_indexA].w; float32 aB = data.positions[m_indexB].a; b2Vec2 vB = data.velocities[m_indexB].v; float32 wB = data.velocities[m_indexB].w; b2Rot qA(aA), qB(aB); m_rA = b2Mul(qA, m_localAnchorA - m_localCenterA); m_rB = b2Mul(qB, m_localAnchorB - m_localCenterB); // J = [-I -r1_skew I r2_skew] // [ 0 -1 0 1] // r_skew = [-ry; rx] // Matlab // K = [ mA+r1y^2*iA+mB+r2y^2*iB, -r1y*iA*r1x-r2y*iB*r2x, -r1y*iA-r2y*iB] // [ -r1y*iA*r1x-r2y*iB*r2x, mA+r1x^2*iA+mB+r2x^2*iB, r1x*iA+r2x*iB] // [ -r1y*iA-r2y*iB, r1x*iA+r2x*iB, iA+iB] float32 mA = m_invMassA, mB = m_invMassB; float32 iA = m_invIA, iB = m_invIB; bool fixedRotation = (iA + iB == 0.0f); m_mass.ex.x = mA + mB + m_rA.y * m_rA.y * iA + m_rB.y * m_rB.y * iB; m_mass.ey.x = -m_rA.y * m_rA.x * iA - m_rB.y * m_rB.x * iB; m_mass.ez.x = -m_rA.y * iA - m_rB.y * iB; m_mass.ex.y = m_mass.ey.x; m_mass.ey.y = mA + mB + m_rA.x * m_rA.x * iA + m_rB.x * m_rB.x * iB; m_mass.ez.y = m_rA.x * iA + m_rB.x * iB; m_mass.ex.z = m_mass.ez.x; m_mass.ey.z = m_mass.ez.y; m_mass.ez.z = iA + iB; m_motorMass = iA + iB; if (m_motorMass > 0.0f) { m_motorMass = 1.0f / m_motorMass; } if (m_enableMotor == false || fixedRotation) { m_motorImpulse = 0.0f; } if (m_enableLimit && fixedRotation == false) { float32 jointAngle = aB - aA - m_referenceAngle; if (b2Abs(m_upperAngle - m_lowerAngle) < 2.0f * b2_angularSlop) { m_limitState = e_equalLimits; } else if (jointAngle <= m_lowerAngle) { if (m_limitState != e_atLowerLimit) { m_impulse.z = 0.0f; } m_limitState = e_atLowerLimit; } else if (jointAngle >= m_upperAngle) { if (m_limitState != e_atUpperLimit) { m_impulse.z = 0.0f; } m_limitState = e_atUpperLimit; } else { m_limitState = e_inactiveLimit; m_impulse.z = 0.0f; } } else { m_limitState = e_inactiveLimit; } if (data.step.warmStarting) { // Scale impulses to support a variable time step. m_impulse *= data.step.dtRatio; m_motorImpulse *= data.step.dtRatio; b2Vec2 P(m_impulse.x, m_impulse.y); vA -= mA * P; wA -= iA * (b2Cross(m_rA, P) + m_motorImpulse + m_impulse.z); vB += mB * P; wB += iB * (b2Cross(m_rB, P) + m_motorImpulse + m_impulse.z); } else { m_impulse.SetZero(); m_motorImpulse = 0.0f; } data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; } void b2RevoluteJoint::SolveVelocityConstraints(const b2SolverData& data) { b2Vec2 vA = data.velocities[m_indexA].v; float32 wA = data.velocities[m_indexA].w; b2Vec2 vB = data.velocities[m_indexB].v; float32 wB = data.velocities[m_indexB].w; float32 mA = m_invMassA, mB = m_invMassB; float32 iA = m_invIA, iB = m_invIB; bool fixedRotation = (iA + iB == 0.0f); // Solve motor constraint. if (m_enableMotor && m_limitState != e_equalLimits && fixedRotation == false) { float32 Cdot = wB - wA - m_motorSpeed; float32 impulse = -m_motorMass * Cdot; float32 oldImpulse = m_motorImpulse; float32 maxImpulse = data.step.dt * m_maxMotorTorque; m_motorImpulse = b2Clamp(m_motorImpulse + impulse, -maxImpulse, maxImpulse); impulse = m_motorImpulse - oldImpulse; wA -= iA * impulse; wB += iB * impulse; } // Solve limit constraint. if (m_enableLimit && m_limitState != e_inactiveLimit && fixedRotation == false) { b2Vec2 Cdot1 = vB + b2Cross(wB, m_rB) - vA - b2Cross(wA, m_rA); float32 Cdot2 = wB - wA; b2Vec3 Cdot(Cdot1.x, Cdot1.y, Cdot2); b2Vec3 impulse = -m_mass.Solve33(Cdot); if (m_limitState == e_equalLimits) { m_impulse += impulse; } else if (m_limitState == e_atLowerLimit) { float32 newImpulse = m_impulse.z + impulse.z; if (newImpulse < 0.0f) { b2Vec2 rhs = -Cdot1 + m_impulse.z * b2Vec2(m_mass.ez.x, m_mass.ez.y); b2Vec2 reduced = m_mass.Solve22(rhs); impulse.x = reduced.x; impulse.y = reduced.y; impulse.z = -m_impulse.z; m_impulse.x += reduced.x; m_impulse.y += reduced.y; m_impulse.z = 0.0f; } else { m_impulse += impulse; } } else if (m_limitState == e_atUpperLimit) { float32 newImpulse = m_impulse.z + impulse.z; if (newImpulse > 0.0f) { b2Vec2 rhs = -Cdot1 + m_impulse.z * b2Vec2(m_mass.ez.x, m_mass.ez.y); b2Vec2 reduced = m_mass.Solve22(rhs); impulse.x = reduced.x; impulse.y = reduced.y; impulse.z = -m_impulse.z; m_impulse.x += reduced.x; m_impulse.y += reduced.y; m_impulse.z = 0.0f; } else { m_impulse += impulse; } } b2Vec2 P(impulse.x, impulse.y); vA -= mA * P; wA -= iA * (b2Cross(m_rA, P) + impulse.z); vB += mB * P; wB += iB * (b2Cross(m_rB, P) + impulse.z); } else { // Solve point-to-point constraint b2Vec2 Cdot = vB + b2Cross(wB, m_rB) - vA - b2Cross(wA, m_rA); b2Vec2 impulse = m_mass.Solve22(-Cdot); m_impulse.x += impulse.x; m_impulse.y += impulse.y; vA -= mA * impulse; wA -= iA * b2Cross(m_rA, impulse); vB += mB * impulse; wB += iB * b2Cross(m_rB, impulse); } data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; } bool b2RevoluteJoint::SolvePositionConstraints(const b2SolverData& data) { b2Vec2 cA = data.positions[m_indexA].c; float32 aA = data.positions[m_indexA].a; b2Vec2 cB = data.positions[m_indexB].c; float32 aB = data.positions[m_indexB].a; b2Rot qA(aA), qB(aB); float32 angularError = 0.0f; float32 positionError = 0.0f; bool fixedRotation = (m_invIA + m_invIB == 0.0f); // Solve angular limit constraint. if (m_enableLimit && m_limitState != e_inactiveLimit && fixedRotation == false) { float32 angle = aB - aA - m_referenceAngle; float32 limitImpulse = 0.0f; if (m_limitState == e_equalLimits) { // Prevent large angular corrections float32 C = b2Clamp(angle - m_lowerAngle, -b2_maxAngularCorrection, b2_maxAngularCorrection); limitImpulse = -m_motorMass * C; angularError = b2Abs(C); } else if (m_limitState == e_atLowerLimit) { float32 C = angle - m_lowerAngle; angularError = -C; // Prevent large angular corrections and allow some slop. C = b2Clamp(C + b2_angularSlop, -b2_maxAngularCorrection, 0.0f); limitImpulse = -m_motorMass * C; } else if (m_limitState == e_atUpperLimit) { float32 C = angle - m_upperAngle; angularError = C; // Prevent large angular corrections and allow some slop. C = b2Clamp(C - b2_angularSlop, 0.0f, b2_maxAngularCorrection); limitImpulse = -m_motorMass * C; } aA -= m_invIA * limitImpulse; aB += m_invIB * limitImpulse; } // Solve point-to-point constraint. { qA.Set(aA); qB.Set(aB); b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA); b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB); b2Vec2 C = cB + rB - cA - rA; positionError = C.Length(); float32 mA = m_invMassA, mB = m_invMassB; float32 iA = m_invIA, iB = m_invIB; b2Mat22 K; K.ex.x = mA + mB + iA * rA.y * rA.y + iB * rB.y * rB.y; K.ex.y = -iA * rA.x * rA.y - iB * rB.x * rB.y; K.ey.x = K.ex.y; K.ey.y = mA + mB + iA * rA.x * rA.x + iB * rB.x * rB.x; b2Vec2 impulse = -K.Solve(C); cA -= mA * impulse; aA -= iA * b2Cross(rA, impulse); cB += mB * impulse; aB += iB * b2Cross(rB, impulse); } data.positions[m_indexA].c = cA; data.positions[m_indexA].a = aA; data.positions[m_indexB].c = cB; data.positions[m_indexB].a = aB; return positionError <= b2_linearSlop && angularError <= b2_angularSlop; } b2Vec2 b2RevoluteJoint::GetAnchorA() const { return m_bodyA->GetWorldPoint(m_localAnchorA); } b2Vec2 b2RevoluteJoint::GetAnchorB() const { return m_bodyB->GetWorldPoint(m_localAnchorB); } b2Vec2 b2RevoluteJoint::GetReactionForce(float32 inv_dt) const { b2Vec2 P(m_impulse.x, m_impulse.y); return inv_dt * P; } float32 b2RevoluteJoint::GetReactionTorque(float32 inv_dt) const { return inv_dt * m_impulse.z; } float32 b2RevoluteJoint::GetJointAngle() const { b2Body* bA = m_bodyA; b2Body* bB = m_bodyB; return bB->m_sweep.a - bA->m_sweep.a - m_referenceAngle; } float32 b2RevoluteJoint::GetJointSpeed() const { b2Body* bA = m_bodyA; b2Body* bB = m_bodyB; return bB->m_angularVelocity - bA->m_angularVelocity; } bool b2RevoluteJoint::IsMotorEnabled() const { return m_enableMotor; } void b2RevoluteJoint::EnableMotor(bool flag) { m_bodyA->SetAwake(true); m_bodyB->SetAwake(true); m_enableMotor = flag; } float32 b2RevoluteJoint::GetMotorTorque(float32 inv_dt) const { return inv_dt * m_motorImpulse; } void b2RevoluteJoint::SetMotorSpeed(float32 speed) { m_bodyA->SetAwake(true); m_bodyB->SetAwake(true); m_motorSpeed = speed; } void b2RevoluteJoint::SetMaxMotorTorque(float32 torque) { m_bodyA->SetAwake(true); m_bodyB->SetAwake(true); m_maxMotorTorque = torque; } bool b2RevoluteJoint::IsLimitEnabled() const { return m_enableLimit; } void b2RevoluteJoint::EnableLimit(bool flag) { if (flag != m_enableLimit) { m_bodyA->SetAwake(true); m_bodyB->SetAwake(true); m_enableLimit = flag; m_impulse.z = 0.0f; } } float32 b2RevoluteJoint::GetLowerLimit() const { return m_lowerAngle; } float32 b2RevoluteJoint::GetUpperLimit() const { return m_upperAngle; } void b2RevoluteJoint::SetLimits(float32 lower, float32 upper) { b2Assert(lower <= upper); if (lower != m_lowerAngle || upper != m_upperAngle) { m_bodyA->SetAwake(true); m_bodyB->SetAwake(true); m_impulse.z = 0.0f; m_lowerAngle = lower; m_upperAngle = upper; } } void b2RevoluteJoint::Dump() { int32 indexA = m_bodyA->m_islandIndex; int32 indexB = m_bodyB->m_islandIndex; b2Log(" b2RevoluteJointDef jd;\n"); b2Log(" jd.bodyA = bodies[%d];\n", indexA); b2Log(" jd.bodyB = bodies[%d];\n", indexB); b2Log(" jd.collideConnected = bool(%d);\n", m_collideConnected); b2Log(" jd.localAnchorA.Set(%.15lef, %.15lef);\n", m_localAnchorA.x, m_localAnchorA.y); b2Log(" jd.localAnchorB.Set(%.15lef, %.15lef);\n", m_localAnchorB.x, m_localAnchorB.y); b2Log(" jd.referenceAngle = %.15lef;\n", m_referenceAngle); b2Log(" jd.enableLimit = bool(%d);\n", m_enableLimit); b2Log(" jd.lowerAngle = %.15lef;\n", m_lowerAngle); b2Log(" jd.upperAngle = %.15lef;\n", m_upperAngle); b2Log(" jd.enableMotor = bool(%d);\n", m_enableMotor); b2Log(" jd.motorSpeed = %.15lef;\n", m_motorSpeed); b2Log(" jd.maxMotorTorque = %.15lef;\n", m_maxMotorTorque); b2Log(" joints[%d] = m_world->CreateJoint(&jd);\n", m_index); } ================================================ FILE: app/src/main/cpp/b2Rope.cpp ================================================ /* * Copyright (c) 2011 Erin Catto http://box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Rope/b2Rope.h" #include "isEngine/ext_lib/Box2D/Common/b2Draw.h" b2Rope::b2Rope() { m_count = 0; m_ps = NULL; m_p0s = NULL; m_vs = NULL; m_ims = NULL; m_Ls = NULL; m_as = NULL; m_gravity.SetZero(); m_k2 = 1.0f; m_k3 = 0.1f; } b2Rope::~b2Rope() { b2Free(m_ps); b2Free(m_p0s); b2Free(m_vs); b2Free(m_ims); b2Free(m_Ls); b2Free(m_as); } void b2Rope::Initialize(const b2RopeDef* def) { b2Assert(def->count >= 3); m_count = def->count; m_ps = (b2Vec2*)b2Alloc(m_count * sizeof(b2Vec2)); m_p0s = (b2Vec2*)b2Alloc(m_count * sizeof(b2Vec2)); m_vs = (b2Vec2*)b2Alloc(m_count * sizeof(b2Vec2)); m_ims = (float32*)b2Alloc(m_count * sizeof(float32)); for (int32 i = 0; i < m_count; ++i) { m_ps[i] = def->vertices[i]; m_p0s[i] = def->vertices[i]; m_vs[i].SetZero(); float32 m = def->masses[i]; if (m > 0.0f) { m_ims[i] = 1.0f / m; } else { m_ims[i] = 0.0f; } } int32 count2 = m_count - 1; int32 count3 = m_count - 2; m_Ls = (float32*)b2Alloc(count2 * sizeof(float32)); m_as = (float32*)b2Alloc(count3 * sizeof(float32)); for (int32 i = 0; i < count2; ++i) { b2Vec2 p1 = m_ps[i]; b2Vec2 p2 = m_ps[i+1]; m_Ls[i] = b2Distance(p1, p2); } for (int32 i = 0; i < count3; ++i) { b2Vec2 p1 = m_ps[i]; b2Vec2 p2 = m_ps[i + 1]; b2Vec2 p3 = m_ps[i + 2]; b2Vec2 d1 = p2 - p1; b2Vec2 d2 = p3 - p2; float32 a = b2Cross(d1, d2); float32 b = b2Dot(d1, d2); m_as[i] = b2Atan2(a, b); } m_gravity = def->gravity; m_damping = def->damping; m_k2 = def->k2; m_k3 = def->k3; } void b2Rope::Step(float32 h, int32 iterations) { if (h == 0.0) { return; } float32 d = expf(- h * m_damping); for (int32 i = 0; i < m_count; ++i) { m_p0s[i] = m_ps[i]; if (m_ims[i] > 0.0f) { m_vs[i] += h * m_gravity; } m_vs[i] *= d; m_ps[i] += h * m_vs[i]; } for (int32 i = 0; i < iterations; ++i) { SolveC2(); SolveC3(); SolveC2(); } float32 inv_h = 1.0f / h; for (int32 i = 0; i < m_count; ++i) { m_vs[i] = inv_h * (m_ps[i] - m_p0s[i]); } } void b2Rope::SolveC2() { int32 count2 = m_count - 1; for (int32 i = 0; i < count2; ++i) { b2Vec2 p1 = m_ps[i]; b2Vec2 p2 = m_ps[i + 1]; b2Vec2 d = p2 - p1; float32 L = d.Normalize(); float32 im1 = m_ims[i]; float32 im2 = m_ims[i + 1]; if (im1 + im2 == 0.0f) { continue; } float32 s1 = im1 / (im1 + im2); float32 s2 = im2 / (im1 + im2); p1 -= m_k2 * s1 * (m_Ls[i] - L) * d; p2 += m_k2 * s2 * (m_Ls[i] - L) * d; m_ps[i] = p1; m_ps[i + 1] = p2; } } void b2Rope::SetAngle(float32 angle) { int32 count3 = m_count - 2; for (int32 i = 0; i < count3; ++i) { m_as[i] = angle; } } void b2Rope::SolveC3() { int32 count3 = m_count - 2; for (int32 i = 0; i < count3; ++i) { b2Vec2 p1 = m_ps[i]; b2Vec2 p2 = m_ps[i + 1]; b2Vec2 p3 = m_ps[i + 2]; float32 m1 = m_ims[i]; float32 m2 = m_ims[i + 1]; float32 m3 = m_ims[i + 2]; b2Vec2 d1 = p2 - p1; b2Vec2 d2 = p3 - p2; float32 L1sqr = d1.LengthSquared(); float32 L2sqr = d2.LengthSquared(); if (L1sqr * L2sqr == 0.0f) { continue; } float32 a = b2Cross(d1, d2); float32 b = b2Dot(d1, d2); float32 angle = b2Atan2(a, b); b2Vec2 Jd1 = (-1.0f / L1sqr) * d1.Skew(); b2Vec2 Jd2 = (1.0f / L2sqr) * d2.Skew(); b2Vec2 J1 = -Jd1; b2Vec2 J2 = Jd1 - Jd2; b2Vec2 J3 = Jd2; float32 mass = m1 * b2Dot(J1, J1) + m2 * b2Dot(J2, J2) + m3 * b2Dot(J3, J3); if (mass == 0.0f) { continue; } mass = 1.0f / mass; float32 C = angle - m_as[i]; while (C > b2_pi) { angle -= 2 * b2_pi; C = angle - m_as[i]; } while (C < -b2_pi) { angle += 2.0f * b2_pi; C = angle - m_as[i]; } float32 impulse = - m_k3 * mass * C; p1 += (m1 * impulse) * J1; p2 += (m2 * impulse) * J2; p3 += (m3 * impulse) * J3; m_ps[i] = p1; m_ps[i + 1] = p2; m_ps[i + 2] = p3; } } void b2Rope::Draw(b2Draw* draw) const { b2Color c(0.4f, 0.5f, 0.7f); for (int32 i = 0; i < m_count - 1; ++i) { draw->DrawSegment(m_ps[i], m_ps[i+1], c); } } ================================================ FILE: app/src/main/cpp/b2RopeJoint.cpp ================================================ /* * Copyright (c) 2007-2011 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2RopeJoint.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Body.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2TimeStep.h" // Limit: // C = norm(pB - pA) - L // u = (pB - pA) / norm(pB - pA) // Cdot = dot(u, vB + cross(wB, rB) - vA - cross(wA, rA)) // J = [-u -cross(rA, u) u cross(rB, u)] // K = J * invM * JT // = invMassA + invIA * cross(rA, u)^2 + invMassB + invIB * cross(rB, u)^2 b2RopeJoint::b2RopeJoint(const b2RopeJointDef* def) : b2Joint(def) { m_localAnchorA = def->localAnchorA; m_localAnchorB = def->localAnchorB; m_maxLength = def->maxLength; m_mass = 0.0f; m_impulse = 0.0f; m_state = e_inactiveLimit; m_length = 0.0f; } void b2RopeJoint::InitVelocityConstraints(const b2SolverData& data) { m_indexA = m_bodyA->m_islandIndex; m_indexB = m_bodyB->m_islandIndex; m_localCenterA = m_bodyA->m_sweep.localCenter; m_localCenterB = m_bodyB->m_sweep.localCenter; m_invMassA = m_bodyA->m_invMass; m_invMassB = m_bodyB->m_invMass; m_invIA = m_bodyA->m_invI; m_invIB = m_bodyB->m_invI; b2Vec2 cA = data.positions[m_indexA].c; float32 aA = data.positions[m_indexA].a; b2Vec2 vA = data.velocities[m_indexA].v; float32 wA = data.velocities[m_indexA].w; b2Vec2 cB = data.positions[m_indexB].c; float32 aB = data.positions[m_indexB].a; b2Vec2 vB = data.velocities[m_indexB].v; float32 wB = data.velocities[m_indexB].w; b2Rot qA(aA), qB(aB); m_rA = b2Mul(qA, m_localAnchorA - m_localCenterA); m_rB = b2Mul(qB, m_localAnchorB - m_localCenterB); m_u = cB + m_rB - cA - m_rA; m_length = m_u.Length(); float32 C = m_length - m_maxLength; if (C > 0.0f) { m_state = e_atUpperLimit; } else { m_state = e_inactiveLimit; } if (m_length > b2_linearSlop) { m_u *= 1.0f / m_length; } else { m_u.SetZero(); m_mass = 0.0f; m_impulse = 0.0f; return; } // Compute effective mass. float32 crA = b2Cross(m_rA, m_u); float32 crB = b2Cross(m_rB, m_u); float32 invMass = m_invMassA + m_invIA * crA * crA + m_invMassB + m_invIB * crB * crB; m_mass = invMass != 0.0f ? 1.0f / invMass : 0.0f; if (data.step.warmStarting) { // Scale the impulse to support a variable time step. m_impulse *= data.step.dtRatio; b2Vec2 P = m_impulse * m_u; vA -= m_invMassA * P; wA -= m_invIA * b2Cross(m_rA, P); vB += m_invMassB * P; wB += m_invIB * b2Cross(m_rB, P); } else { m_impulse = 0.0f; } data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; } void b2RopeJoint::SolveVelocityConstraints(const b2SolverData& data) { b2Vec2 vA = data.velocities[m_indexA].v; float32 wA = data.velocities[m_indexA].w; b2Vec2 vB = data.velocities[m_indexB].v; float32 wB = data.velocities[m_indexB].w; // Cdot = dot(u, v + cross(w, r)) b2Vec2 vpA = vA + b2Cross(wA, m_rA); b2Vec2 vpB = vB + b2Cross(wB, m_rB); float32 C = m_length - m_maxLength; float32 Cdot = b2Dot(m_u, vpB - vpA); // Predictive constraint. if (C < 0.0f) { Cdot += data.step.inv_dt * C; } float32 impulse = -m_mass * Cdot; float32 oldImpulse = m_impulse; m_impulse = b2Min(0.0f, m_impulse + impulse); impulse = m_impulse - oldImpulse; b2Vec2 P = impulse * m_u; vA -= m_invMassA * P; wA -= m_invIA * b2Cross(m_rA, P); vB += m_invMassB * P; wB += m_invIB * b2Cross(m_rB, P); data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; } bool b2RopeJoint::SolvePositionConstraints(const b2SolverData& data) { b2Vec2 cA = data.positions[m_indexA].c; float32 aA = data.positions[m_indexA].a; b2Vec2 cB = data.positions[m_indexB].c; float32 aB = data.positions[m_indexB].a; b2Rot qA(aA), qB(aB); b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA); b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB); b2Vec2 u = cB + rB - cA - rA; float32 length = u.Normalize(); float32 C = length - m_maxLength; C = b2Clamp(C, 0.0f, b2_maxLinearCorrection); float32 impulse = -m_mass * C; b2Vec2 P = impulse * u; cA -= m_invMassA * P; aA -= m_invIA * b2Cross(rA, P); cB += m_invMassB * P; aB += m_invIB * b2Cross(rB, P); data.positions[m_indexA].c = cA; data.positions[m_indexA].a = aA; data.positions[m_indexB].c = cB; data.positions[m_indexB].a = aB; return length - m_maxLength < b2_linearSlop; } b2Vec2 b2RopeJoint::GetAnchorA() const { return m_bodyA->GetWorldPoint(m_localAnchorA); } b2Vec2 b2RopeJoint::GetAnchorB() const { return m_bodyB->GetWorldPoint(m_localAnchorB); } b2Vec2 b2RopeJoint::GetReactionForce(float32 inv_dt) const { b2Vec2 F = (inv_dt * m_impulse) * m_u; return F; } float32 b2RopeJoint::GetReactionTorque(float32 inv_dt) const { B2_NOT_USED(inv_dt); return 0.0f; } float32 b2RopeJoint::GetMaxLength() const { return m_maxLength; } b2LimitState b2RopeJoint::GetLimitState() const { return m_state; } void b2RopeJoint::Dump() { int32 indexA = m_bodyA->m_islandIndex; int32 indexB = m_bodyB->m_islandIndex; b2Log(" b2RopeJointDef jd;\n"); b2Log(" jd.bodyA = bodies[%d];\n", indexA); b2Log(" jd.bodyB = bodies[%d];\n", indexB); b2Log(" jd.collideConnected = bool(%d);\n", m_collideConnected); b2Log(" jd.localAnchorA.Set(%.15lef, %.15lef);\n", m_localAnchorA.x, m_localAnchorA.y); b2Log(" jd.localAnchorB.Set(%.15lef, %.15lef);\n", m_localAnchorB.x, m_localAnchorB.y); b2Log(" jd.maxLength = %.15lef;\n", m_maxLength); b2Log(" joints[%d] = m_world->CreateJoint(&jd);\n", m_index); } ================================================ FILE: app/src/main/cpp/b2Settings.cpp ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Common/b2Settings.h" #include #include #include b2Version b2_version = {2, 3, 0}; // Memory allocators. Modify these to use your own allocator. void* b2Alloc(int32 size) { return malloc(size); } void b2Free(void* mem) { free(mem); } // You can modify this to use your logging facility. void b2Log(const char* string, ...) { va_list args; va_start(args, string); vprintf(string, args); va_end(args); } ================================================ FILE: app/src/main/cpp/b2StackAllocator.cpp ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Common/b2StackAllocator.h" #include "isEngine/ext_lib/Box2D/Common/b2Math.h" b2StackAllocator::b2StackAllocator() { m_index = 0; m_allocation = 0; m_maxAllocation = 0; m_entryCount = 0; } b2StackAllocator::~b2StackAllocator() { b2Assert(m_index == 0); b2Assert(m_entryCount == 0); } void* b2StackAllocator::Allocate(int32 size) { b2Assert(m_entryCount < b2_maxStackEntries); b2StackEntry* entry = m_entries + m_entryCount; entry->size = size; if (m_index + size > b2_stackSize) { entry->data = (char*)b2Alloc(size); entry->usedMalloc = true; } else { entry->data = m_data + m_index; entry->usedMalloc = false; m_index += size; } m_allocation += size; m_maxAllocation = b2Max(m_maxAllocation, m_allocation); ++m_entryCount; return entry->data; } void b2StackAllocator::Free(void* p) { b2Assert(m_entryCount > 0); b2StackEntry* entry = m_entries + m_entryCount - 1; b2Assert(p == entry->data); if (entry->usedMalloc) { b2Free(p); } else { m_index -= entry->size; } m_allocation -= entry->size; --m_entryCount; p = NULL; } int32 b2StackAllocator::GetMaxAllocation() const { return m_maxAllocation; } ================================================ FILE: app/src/main/cpp/b2TimeOfImpact.cpp ================================================ /* * Copyright (c) 2007-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Collision/b2Collision.h" #include "isEngine/ext_lib/Box2D/Collision/b2Distance.h" #include "isEngine/ext_lib/Box2D/Collision/b2TimeOfImpact.h" #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2CircleShape.h" #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2PolygonShape.h" #include "isEngine/ext_lib/Box2D/Common/b2Timer.h" #include float32 b2_toiTime, b2_toiMaxTime; int32 b2_toiCalls, b2_toiIters, b2_toiMaxIters; int32 b2_toiRootIters, b2_toiMaxRootIters; // struct b2SeparationFunction { enum Type { e_points, e_faceA, e_faceB }; // TODO_ERIN might not need to return the separation float32 Initialize(const b2SimplexCache* cache, const b2DistanceProxy* proxyA, const b2Sweep& sweepA, const b2DistanceProxy* proxyB, const b2Sweep& sweepB, float32 t1) { m_proxyA = proxyA; m_proxyB = proxyB; int32 count = cache->count; b2Assert(0 < count && count < 3); m_sweepA = sweepA; m_sweepB = sweepB; b2Transform xfA, xfB; m_sweepA.GetTransform(&xfA, t1); m_sweepB.GetTransform(&xfB, t1); if (count == 1) { m_type = e_points; b2Vec2 localPointA = m_proxyA->GetVertex(cache->indexA[0]); b2Vec2 localPointB = m_proxyB->GetVertex(cache->indexB[0]); b2Vec2 pointA = b2Mul(xfA, localPointA); b2Vec2 pointB = b2Mul(xfB, localPointB); m_axis = pointB - pointA; float32 s = m_axis.Normalize(); return s; } else if (cache->indexA[0] == cache->indexA[1]) { // Two points on B and one on A. m_type = e_faceB; b2Vec2 localPointB1 = proxyB->GetVertex(cache->indexB[0]); b2Vec2 localPointB2 = proxyB->GetVertex(cache->indexB[1]); m_axis = b2Cross(localPointB2 - localPointB1, 1.0f); m_axis.Normalize(); b2Vec2 normal = b2Mul(xfB.q, m_axis); m_localPoint = 0.5f * (localPointB1 + localPointB2); b2Vec2 pointB = b2Mul(xfB, m_localPoint); b2Vec2 localPointA = proxyA->GetVertex(cache->indexA[0]); b2Vec2 pointA = b2Mul(xfA, localPointA); float32 s = b2Dot(pointA - pointB, normal); if (s < 0.0f) { m_axis = -m_axis; s = -s; } return s; } else { // Two points on A and one or two points on B. m_type = e_faceA; b2Vec2 localPointA1 = m_proxyA->GetVertex(cache->indexA[0]); b2Vec2 localPointA2 = m_proxyA->GetVertex(cache->indexA[1]); m_axis = b2Cross(localPointA2 - localPointA1, 1.0f); m_axis.Normalize(); b2Vec2 normal = b2Mul(xfA.q, m_axis); m_localPoint = 0.5f * (localPointA1 + localPointA2); b2Vec2 pointA = b2Mul(xfA, m_localPoint); b2Vec2 localPointB = m_proxyB->GetVertex(cache->indexB[0]); b2Vec2 pointB = b2Mul(xfB, localPointB); float32 s = b2Dot(pointB - pointA, normal); if (s < 0.0f) { m_axis = -m_axis; s = -s; } return s; } } // float32 FindMinSeparation(int32* indexA, int32* indexB, float32 t) const { b2Transform xfA, xfB; m_sweepA.GetTransform(&xfA, t); m_sweepB.GetTransform(&xfB, t); switch (m_type) { case e_points: { b2Vec2 axisA = b2MulT(xfA.q, m_axis); b2Vec2 axisB = b2MulT(xfB.q, -m_axis); *indexA = m_proxyA->GetSupport(axisA); *indexB = m_proxyB->GetSupport(axisB); b2Vec2 localPointA = m_proxyA->GetVertex(*indexA); b2Vec2 localPointB = m_proxyB->GetVertex(*indexB); b2Vec2 pointA = b2Mul(xfA, localPointA); b2Vec2 pointB = b2Mul(xfB, localPointB); float32 separation = b2Dot(pointB - pointA, m_axis); return separation; } case e_faceA: { b2Vec2 normal = b2Mul(xfA.q, m_axis); b2Vec2 pointA = b2Mul(xfA, m_localPoint); b2Vec2 axisB = b2MulT(xfB.q, -normal); *indexA = -1; *indexB = m_proxyB->GetSupport(axisB); b2Vec2 localPointB = m_proxyB->GetVertex(*indexB); b2Vec2 pointB = b2Mul(xfB, localPointB); float32 separation = b2Dot(pointB - pointA, normal); return separation; } case e_faceB: { b2Vec2 normal = b2Mul(xfB.q, m_axis); b2Vec2 pointB = b2Mul(xfB, m_localPoint); b2Vec2 axisA = b2MulT(xfA.q, -normal); *indexB = -1; *indexA = m_proxyA->GetSupport(axisA); b2Vec2 localPointA = m_proxyA->GetVertex(*indexA); b2Vec2 pointA = b2Mul(xfA, localPointA); float32 separation = b2Dot(pointA - pointB, normal); return separation; } default: b2Assert(false); *indexA = -1; *indexB = -1; return 0.0f; } } // float32 Evaluate(int32 indexA, int32 indexB, float32 t) const { b2Transform xfA, xfB; m_sweepA.GetTransform(&xfA, t); m_sweepB.GetTransform(&xfB, t); switch (m_type) { case e_points: { b2Vec2 localPointA = m_proxyA->GetVertex(indexA); b2Vec2 localPointB = m_proxyB->GetVertex(indexB); b2Vec2 pointA = b2Mul(xfA, localPointA); b2Vec2 pointB = b2Mul(xfB, localPointB); float32 separation = b2Dot(pointB - pointA, m_axis); return separation; } case e_faceA: { b2Vec2 normal = b2Mul(xfA.q, m_axis); b2Vec2 pointA = b2Mul(xfA, m_localPoint); b2Vec2 localPointB = m_proxyB->GetVertex(indexB); b2Vec2 pointB = b2Mul(xfB, localPointB); float32 separation = b2Dot(pointB - pointA, normal); return separation; } case e_faceB: { b2Vec2 normal = b2Mul(xfB.q, m_axis); b2Vec2 pointB = b2Mul(xfB, m_localPoint); b2Vec2 localPointA = m_proxyA->GetVertex(indexA); b2Vec2 pointA = b2Mul(xfA, localPointA); float32 separation = b2Dot(pointA - pointB, normal); return separation; } default: b2Assert(false); return 0.0f; } } const b2DistanceProxy* m_proxyA; const b2DistanceProxy* m_proxyB; b2Sweep m_sweepA, m_sweepB; Type m_type; b2Vec2 m_localPoint; b2Vec2 m_axis; }; // CCD via the local separating axis method. This seeks progression // by computing the largest time at which separation is maintained. void b2TimeOfImpact(b2TOIOutput* output, const b2TOIInput* input) { b2Timer timer; ++b2_toiCalls; output->state = b2TOIOutput::e_unknown; output->t = input->tMax; const b2DistanceProxy* proxyA = &input->proxyA; const b2DistanceProxy* proxyB = &input->proxyB; b2Sweep sweepA = input->sweepA; b2Sweep sweepB = input->sweepB; // Large rotations can make the root finder fail, so we normalize the // sweep angles. sweepA.Normalize(); sweepB.Normalize(); float32 tMax = input->tMax; float32 totalRadius = proxyA->m_radius + proxyB->m_radius; float32 target = b2Max(b2_linearSlop, totalRadius - 3.0f * b2_linearSlop); float32 tolerance = 0.25f * b2_linearSlop; b2Assert(target > tolerance); float32 t1 = 0.0f; const int32 k_maxIterations = 20; // TODO_ERIN b2Settings int32 iter = 0; // Prepare input for distance query. b2SimplexCache cache; cache.count = 0; b2DistanceInput distanceInput; distanceInput.proxyA = input->proxyA; distanceInput.proxyB = input->proxyB; distanceInput.useRadii = false; // The outer loop progressively attempts to compute new separating axes. // This loop terminates when an axis is repeated (no progress is made). for(;;) { b2Transform xfA, xfB; sweepA.GetTransform(&xfA, t1); sweepB.GetTransform(&xfB, t1); // Get the distance between shapes. We can also use the results // to get a separating axis. distanceInput.transformA = xfA; distanceInput.transformB = xfB; b2DistanceOutput distanceOutput; b2Distance(&distanceOutput, &cache, &distanceInput); // If the shapes are overlapped, we give up on continuous collision. if (distanceOutput.distance <= 0.0f) { // Failure! output->state = b2TOIOutput::e_overlapped; output->t = 0.0f; break; } if (distanceOutput.distance < target + tolerance) { // Victory! output->state = b2TOIOutput::e_touching; output->t = t1; break; } // Initialize the separating axis. b2SeparationFunction fcn; fcn.Initialize(&cache, proxyA, sweepA, proxyB, sweepB, t1); #if 0 // Dump the curve seen by the root finder { const int32 N = 100; float32 dx = 1.0f / N; float32 xs[N+1]; float32 fs[N+1]; float32 x = 0.0f; for (int32 i = 0; i <= N; ++i) { sweepA.GetTransform(&xfA, x); sweepB.GetTransform(&xfB, x); float32 f = fcn.Evaluate(xfA, xfB) - target; printf("%g %g\n", x, f); xs[i] = x; fs[i] = f; x += dx; } } #endif // Compute the TOI on the separating axis. We do this by successively // resolving the deepest point. This loop is bounded by the number of vertices. bool done = false; float32 t2 = tMax; int32 pushBackIter = 0; for (;;) { // Find the deepest point at t2. Store the witness point indices. int32 indexA, indexB; float32 s2 = fcn.FindMinSeparation(&indexA, &indexB, t2); // Is the final configuration separated? if (s2 > target + tolerance) { // Victory! output->state = b2TOIOutput::e_separated; output->t = tMax; done = true; break; } // Has the separation reached tolerance? if (s2 > target - tolerance) { // Advance the sweeps t1 = t2; break; } // Compute the initial separation of the witness points. float32 s1 = fcn.Evaluate(indexA, indexB, t1); // Check for initial overlap. This might happen if the root finder // runs out of iterations. if (s1 < target - tolerance) { output->state = b2TOIOutput::e_failed; output->t = t1; done = true; break; } // Check for touching if (s1 <= target + tolerance) { // Victory! t1 should hold the TOI (could be 0.0). output->state = b2TOIOutput::e_touching; output->t = t1; done = true; break; } // Compute 1D root of: f(x) - target = 0 int32 rootIterCount = 0; float32 a1 = t1, a2 = t2; for (;;) { // Use a mix of the secant rule and bisection. float32 t; if (rootIterCount & 1) { // Secant rule to improve convergence. t = a1 + (target - s1) * (a2 - a1) / (s2 - s1); } else { // Bisection to guarantee progress. t = 0.5f * (a1 + a2); } ++rootIterCount; ++b2_toiRootIters; float32 s = fcn.Evaluate(indexA, indexB, t); if (b2Abs(s - target) < tolerance) { // t2 holds a tentative value for t1 t2 = t; break; } // Ensure we continue to bracket the root. if (s > target) { a1 = t; s1 = s; } else { a2 = t; s2 = s; } if (rootIterCount == 50) { break; } } b2_toiMaxRootIters = b2Max(b2_toiMaxRootIters, rootIterCount); ++pushBackIter; if (pushBackIter == b2_maxPolygonVertices) { break; } } ++iter; ++b2_toiIters; if (done) { break; } if (iter == k_maxIterations) { // Root finder got stuck. Semi-victory. output->state = b2TOIOutput::e_failed; output->t = t1; break; } } b2_toiMaxIters = b2Max(b2_toiMaxIters, iter); float32 time = timer.GetMilliseconds(); b2_toiMaxTime = b2Max(b2_toiMaxTime, time); b2_toiTime += time; } ================================================ FILE: app/src/main/cpp/b2Timer.cpp ================================================ /* * Copyright (c) 2011 Erin Catto http://box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Common/b2Timer.h" #if defined(_WIN32) float64 b2Timer::s_invFrequency = 0.0f; #define WIN32_LEAN_AND_MEAN #include b2Timer::b2Timer() { LARGE_INTEGER largeInteger; if (s_invFrequency == 0.0f) { QueryPerformanceFrequency(&largeInteger); s_invFrequency = float64(largeInteger.QuadPart); if (s_invFrequency > 0.0f) { s_invFrequency = 1000.0f / s_invFrequency; } } QueryPerformanceCounter(&largeInteger); m_start = float64(largeInteger.QuadPart); } void b2Timer::Reset() { LARGE_INTEGER largeInteger; QueryPerformanceCounter(&largeInteger); m_start = float64(largeInteger.QuadPart); } float32 b2Timer::GetMilliseconds() const { LARGE_INTEGER largeInteger; QueryPerformanceCounter(&largeInteger); float64 count = float64(largeInteger.QuadPart); float32 ms = float32(s_invFrequency * (count - m_start)); return ms; } #elif defined(__linux__) || defined (__APPLE__) #include b2Timer::b2Timer() { Reset(); } void b2Timer::Reset() { timeval t; gettimeofday(&t, 0); m_start_sec = t.tv_sec; m_start_usec = t.tv_usec; } float32 b2Timer::GetMilliseconds() const { timeval t; gettimeofday(&t, 0); return 1000.0f * (t.tv_sec - m_start_sec) + 0.001f * (t.tv_usec - m_start_usec); } #else b2Timer::b2Timer() { } void b2Timer::Reset() { } float32 b2Timer::GetMilliseconds() const { return 0.0f; } #endif ================================================ FILE: app/src/main/cpp/b2WeldJoint.cpp ================================================ /* * Copyright (c) 2006-2011 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2WeldJoint.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Body.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2TimeStep.h" // Point-to-point constraint // C = p2 - p1 // Cdot = v2 - v1 // = v2 + cross(w2, r2) - v1 - cross(w1, r1) // J = [-I -r1_skew I r2_skew ] // Identity used: // w k % (rx i + ry j) = w * (-ry i + rx j) // Angle constraint // C = angle2 - angle1 - referenceAngle // Cdot = w2 - w1 // J = [0 0 -1 0 0 1] // K = invI1 + invI2 void b2WeldJointDef::Initialize(b2Body* bA, b2Body* bB, const b2Vec2& anchor) { bodyA = bA; bodyB = bB; localAnchorA = bodyA->GetLocalPoint(anchor); localAnchorB = bodyB->GetLocalPoint(anchor); referenceAngle = bodyB->GetAngle() - bodyA->GetAngle(); } b2WeldJoint::b2WeldJoint(const b2WeldJointDef* def) : b2Joint(def) { m_localAnchorA = def->localAnchorA; m_localAnchorB = def->localAnchorB; m_referenceAngle = def->referenceAngle; m_frequencyHz = def->frequencyHz; m_dampingRatio = def->dampingRatio; m_impulse.SetZero(); } void b2WeldJoint::InitVelocityConstraints(const b2SolverData& data) { m_indexA = m_bodyA->m_islandIndex; m_indexB = m_bodyB->m_islandIndex; m_localCenterA = m_bodyA->m_sweep.localCenter; m_localCenterB = m_bodyB->m_sweep.localCenter; m_invMassA = m_bodyA->m_invMass; m_invMassB = m_bodyB->m_invMass; m_invIA = m_bodyA->m_invI; m_invIB = m_bodyB->m_invI; float32 aA = data.positions[m_indexA].a; b2Vec2 vA = data.velocities[m_indexA].v; float32 wA = data.velocities[m_indexA].w; float32 aB = data.positions[m_indexB].a; b2Vec2 vB = data.velocities[m_indexB].v; float32 wB = data.velocities[m_indexB].w; b2Rot qA(aA), qB(aB); m_rA = b2Mul(qA, m_localAnchorA - m_localCenterA); m_rB = b2Mul(qB, m_localAnchorB - m_localCenterB); // J = [-I -r1_skew I r2_skew] // [ 0 -1 0 1] // r_skew = [-ry; rx] // Matlab // K = [ mA+r1y^2*iA+mB+r2y^2*iB, -r1y*iA*r1x-r2y*iB*r2x, -r1y*iA-r2y*iB] // [ -r1y*iA*r1x-r2y*iB*r2x, mA+r1x^2*iA+mB+r2x^2*iB, r1x*iA+r2x*iB] // [ -r1y*iA-r2y*iB, r1x*iA+r2x*iB, iA+iB] float32 mA = m_invMassA, mB = m_invMassB; float32 iA = m_invIA, iB = m_invIB; b2Mat33 K; K.ex.x = mA + mB + m_rA.y * m_rA.y * iA + m_rB.y * m_rB.y * iB; K.ey.x = -m_rA.y * m_rA.x * iA - m_rB.y * m_rB.x * iB; K.ez.x = -m_rA.y * iA - m_rB.y * iB; K.ex.y = K.ey.x; K.ey.y = mA + mB + m_rA.x * m_rA.x * iA + m_rB.x * m_rB.x * iB; K.ez.y = m_rA.x * iA + m_rB.x * iB; K.ex.z = K.ez.x; K.ey.z = K.ez.y; K.ez.z = iA + iB; if (m_frequencyHz > 0.0f) { K.GetInverse22(&m_mass); float32 invM = iA + iB; float32 m = invM > 0.0f ? 1.0f / invM : 0.0f; float32 C = aB - aA - m_referenceAngle; // Frequency float32 omega = 2.0f * b2_pi * m_frequencyHz; // Damping coefficient float32 d = 2.0f * m * m_dampingRatio * omega; // Spring stiffness float32 k = m * omega * omega; // magic formulas float32 h = data.step.dt; m_gamma = h * (d + h * k); m_gamma = m_gamma != 0.0f ? 1.0f / m_gamma : 0.0f; m_bias = C * h * k * m_gamma; invM += m_gamma; m_mass.ez.z = invM != 0.0f ? 1.0f / invM : 0.0f; } else { K.GetSymInverse33(&m_mass); m_gamma = 0.0f; m_bias = 0.0f; } if (data.step.warmStarting) { // Scale impulses to support a variable time step. m_impulse *= data.step.dtRatio; b2Vec2 P(m_impulse.x, m_impulse.y); vA -= mA * P; wA -= iA * (b2Cross(m_rA, P) + m_impulse.z); vB += mB * P; wB += iB * (b2Cross(m_rB, P) + m_impulse.z); } else { m_impulse.SetZero(); } data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; } void b2WeldJoint::SolveVelocityConstraints(const b2SolverData& data) { b2Vec2 vA = data.velocities[m_indexA].v; float32 wA = data.velocities[m_indexA].w; b2Vec2 vB = data.velocities[m_indexB].v; float32 wB = data.velocities[m_indexB].w; float32 mA = m_invMassA, mB = m_invMassB; float32 iA = m_invIA, iB = m_invIB; if (m_frequencyHz > 0.0f) { float32 Cdot2 = wB - wA; float32 impulse2 = -m_mass.ez.z * (Cdot2 + m_bias + m_gamma * m_impulse.z); m_impulse.z += impulse2; wA -= iA * impulse2; wB += iB * impulse2; b2Vec2 Cdot1 = vB + b2Cross(wB, m_rB) - vA - b2Cross(wA, m_rA); b2Vec2 impulse1 = -b2Mul22(m_mass, Cdot1); m_impulse.x += impulse1.x; m_impulse.y += impulse1.y; b2Vec2 P = impulse1; vA -= mA * P; wA -= iA * b2Cross(m_rA, P); vB += mB * P; wB += iB * b2Cross(m_rB, P); } else { b2Vec2 Cdot1 = vB + b2Cross(wB, m_rB) - vA - b2Cross(wA, m_rA); float32 Cdot2 = wB - wA; b2Vec3 Cdot(Cdot1.x, Cdot1.y, Cdot2); b2Vec3 impulse = -b2Mul(m_mass, Cdot); m_impulse += impulse; b2Vec2 P(impulse.x, impulse.y); vA -= mA * P; wA -= iA * (b2Cross(m_rA, P) + impulse.z); vB += mB * P; wB += iB * (b2Cross(m_rB, P) + impulse.z); } data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; } bool b2WeldJoint::SolvePositionConstraints(const b2SolverData& data) { b2Vec2 cA = data.positions[m_indexA].c; float32 aA = data.positions[m_indexA].a; b2Vec2 cB = data.positions[m_indexB].c; float32 aB = data.positions[m_indexB].a; b2Rot qA(aA), qB(aB); float32 mA = m_invMassA, mB = m_invMassB; float32 iA = m_invIA, iB = m_invIB; b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA); b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB); float32 positionError, angularError; b2Mat33 K; K.ex.x = mA + mB + rA.y * rA.y * iA + rB.y * rB.y * iB; K.ey.x = -rA.y * rA.x * iA - rB.y * rB.x * iB; K.ez.x = -rA.y * iA - rB.y * iB; K.ex.y = K.ey.x; K.ey.y = mA + mB + rA.x * rA.x * iA + rB.x * rB.x * iB; K.ez.y = rA.x * iA + rB.x * iB; K.ex.z = K.ez.x; K.ey.z = K.ez.y; K.ez.z = iA + iB; if (m_frequencyHz > 0.0f) { b2Vec2 C1 = cB + rB - cA - rA; positionError = C1.Length(); angularError = 0.0f; b2Vec2 P = -K.Solve22(C1); cA -= mA * P; aA -= iA * b2Cross(rA, P); cB += mB * P; aB += iB * b2Cross(rB, P); } else { b2Vec2 C1 = cB + rB - cA - rA; float32 C2 = aB - aA - m_referenceAngle; positionError = C1.Length(); angularError = b2Abs(C2); b2Vec3 C(C1.x, C1.y, C2); b2Vec3 impulse = -K.Solve33(C); b2Vec2 P(impulse.x, impulse.y); cA -= mA * P; aA -= iA * (b2Cross(rA, P) + impulse.z); cB += mB * P; aB += iB * (b2Cross(rB, P) + impulse.z); } data.positions[m_indexA].c = cA; data.positions[m_indexA].a = aA; data.positions[m_indexB].c = cB; data.positions[m_indexB].a = aB; return positionError <= b2_linearSlop && angularError <= b2_angularSlop; } b2Vec2 b2WeldJoint::GetAnchorA() const { return m_bodyA->GetWorldPoint(m_localAnchorA); } b2Vec2 b2WeldJoint::GetAnchorB() const { return m_bodyB->GetWorldPoint(m_localAnchorB); } b2Vec2 b2WeldJoint::GetReactionForce(float32 inv_dt) const { b2Vec2 P(m_impulse.x, m_impulse.y); return inv_dt * P; } float32 b2WeldJoint::GetReactionTorque(float32 inv_dt) const { return inv_dt * m_impulse.z; } void b2WeldJoint::Dump() { int32 indexA = m_bodyA->m_islandIndex; int32 indexB = m_bodyB->m_islandIndex; b2Log(" b2WeldJointDef jd;\n"); b2Log(" jd.bodyA = bodies[%d];\n", indexA); b2Log(" jd.bodyB = bodies[%d];\n", indexB); b2Log(" jd.collideConnected = bool(%d);\n", m_collideConnected); b2Log(" jd.localAnchorA.Set(%.15lef, %.15lef);\n", m_localAnchorA.x, m_localAnchorA.y); b2Log(" jd.localAnchorB.Set(%.15lef, %.15lef);\n", m_localAnchorB.x, m_localAnchorB.y); b2Log(" jd.referenceAngle = %.15lef;\n", m_referenceAngle); b2Log(" jd.frequencyHz = %.15lef;\n", m_frequencyHz); b2Log(" jd.dampingRatio = %.15lef;\n", m_dampingRatio); b2Log(" joints[%d] = m_world->CreateJoint(&jd);\n", m_index); } ================================================ FILE: app/src/main/cpp/b2WheelJoint.cpp ================================================ /* * Copyright (c) 2006-2007 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2WheelJoint.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Body.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2TimeStep.h" // Linear constraint (point-to-line) // d = pB - pA = xB + rB - xA - rA // C = dot(ay, d) // Cdot = dot(d, cross(wA, ay)) + dot(ay, vB + cross(wB, rB) - vA - cross(wA, rA)) // = -dot(ay, vA) - dot(cross(d + rA, ay), wA) + dot(ay, vB) + dot(cross(rB, ay), vB) // J = [-ay, -cross(d + rA, ay), ay, cross(rB, ay)] // Spring linear constraint // C = dot(ax, d) // Cdot = = -dot(ax, vA) - dot(cross(d + rA, ax), wA) + dot(ax, vB) + dot(cross(rB, ax), vB) // J = [-ax -cross(d+rA, ax) ax cross(rB, ax)] // Motor rotational constraint // Cdot = wB - wA // J = [0 0 -1 0 0 1] void b2WheelJointDef::Initialize(b2Body* bA, b2Body* bB, const b2Vec2& anchor, const b2Vec2& axis) { bodyA = bA; bodyB = bB; localAnchorA = bodyA->GetLocalPoint(anchor); localAnchorB = bodyB->GetLocalPoint(anchor); localAxisA = bodyA->GetLocalVector(axis); } b2WheelJoint::b2WheelJoint(const b2WheelJointDef* def) : b2Joint(def) { m_localAnchorA = def->localAnchorA; m_localAnchorB = def->localAnchorB; m_localXAxisA = def->localAxisA; m_localYAxisA = b2Cross(1.0f, m_localXAxisA); m_mass = 0.0f; m_impulse = 0.0f; m_motorMass = 0.0f; m_motorImpulse = 0.0f; m_springMass = 0.0f; m_springImpulse = 0.0f; m_maxMotorTorque = def->maxMotorTorque; m_motorSpeed = def->motorSpeed; m_enableMotor = def->enableMotor; m_frequencyHz = def->frequencyHz; m_dampingRatio = def->dampingRatio; m_bias = 0.0f; m_gamma = 0.0f; m_ax.SetZero(); m_ay.SetZero(); } void b2WheelJoint::InitVelocityConstraints(const b2SolverData& data) { m_indexA = m_bodyA->m_islandIndex; m_indexB = m_bodyB->m_islandIndex; m_localCenterA = m_bodyA->m_sweep.localCenter; m_localCenterB = m_bodyB->m_sweep.localCenter; m_invMassA = m_bodyA->m_invMass; m_invMassB = m_bodyB->m_invMass; m_invIA = m_bodyA->m_invI; m_invIB = m_bodyB->m_invI; float32 mA = m_invMassA, mB = m_invMassB; float32 iA = m_invIA, iB = m_invIB; b2Vec2 cA = data.positions[m_indexA].c; float32 aA = data.positions[m_indexA].a; b2Vec2 vA = data.velocities[m_indexA].v; float32 wA = data.velocities[m_indexA].w; b2Vec2 cB = data.positions[m_indexB].c; float32 aB = data.positions[m_indexB].a; b2Vec2 vB = data.velocities[m_indexB].v; float32 wB = data.velocities[m_indexB].w; b2Rot qA(aA), qB(aB); // Compute the effective masses. b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA); b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB); b2Vec2 d = cB + rB - cA - rA; // Point to line constraint { m_ay = b2Mul(qA, m_localYAxisA); m_sAy = b2Cross(d + rA, m_ay); m_sBy = b2Cross(rB, m_ay); m_mass = mA + mB + iA * m_sAy * m_sAy + iB * m_sBy * m_sBy; if (m_mass > 0.0f) { m_mass = 1.0f / m_mass; } } // Spring constraint m_springMass = 0.0f; m_bias = 0.0f; m_gamma = 0.0f; if (m_frequencyHz > 0.0f) { m_ax = b2Mul(qA, m_localXAxisA); m_sAx = b2Cross(d + rA, m_ax); m_sBx = b2Cross(rB, m_ax); float32 invMass = mA + mB + iA * m_sAx * m_sAx + iB * m_sBx * m_sBx; if (invMass > 0.0f) { m_springMass = 1.0f / invMass; float32 C = b2Dot(d, m_ax); // Frequency float32 omega = 2.0f * b2_pi * m_frequencyHz; // Damping coefficient float32 d = 2.0f * m_springMass * m_dampingRatio * omega; // Spring stiffness float32 k = m_springMass * omega * omega; // magic formulas float32 h = data.step.dt; m_gamma = h * (d + h * k); if (m_gamma > 0.0f) { m_gamma = 1.0f / m_gamma; } m_bias = C * h * k * m_gamma; m_springMass = invMass + m_gamma; if (m_springMass > 0.0f) { m_springMass = 1.0f / m_springMass; } } } else { m_springImpulse = 0.0f; } // Rotational motor if (m_enableMotor) { m_motorMass = iA + iB; if (m_motorMass > 0.0f) { m_motorMass = 1.0f / m_motorMass; } } else { m_motorMass = 0.0f; m_motorImpulse = 0.0f; } if (data.step.warmStarting) { // Account for variable time step. m_impulse *= data.step.dtRatio; m_springImpulse *= data.step.dtRatio; m_motorImpulse *= data.step.dtRatio; b2Vec2 P = m_impulse * m_ay + m_springImpulse * m_ax; float32 LA = m_impulse * m_sAy + m_springImpulse * m_sAx + m_motorImpulse; float32 LB = m_impulse * m_sBy + m_springImpulse * m_sBx + m_motorImpulse; vA -= m_invMassA * P; wA -= m_invIA * LA; vB += m_invMassB * P; wB += m_invIB * LB; } else { m_impulse = 0.0f; m_springImpulse = 0.0f; m_motorImpulse = 0.0f; } data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; } void b2WheelJoint::SolveVelocityConstraints(const b2SolverData& data) { float32 mA = m_invMassA, mB = m_invMassB; float32 iA = m_invIA, iB = m_invIB; b2Vec2 vA = data.velocities[m_indexA].v; float32 wA = data.velocities[m_indexA].w; b2Vec2 vB = data.velocities[m_indexB].v; float32 wB = data.velocities[m_indexB].w; // Solve spring constraint { float32 Cdot = b2Dot(m_ax, vB - vA) + m_sBx * wB - m_sAx * wA; float32 impulse = -m_springMass * (Cdot + m_bias + m_gamma * m_springImpulse); m_springImpulse += impulse; b2Vec2 P = impulse * m_ax; float32 LA = impulse * m_sAx; float32 LB = impulse * m_sBx; vA -= mA * P; wA -= iA * LA; vB += mB * P; wB += iB * LB; } // Solve rotational motor constraint { float32 Cdot = wB - wA - m_motorSpeed; float32 impulse = -m_motorMass * Cdot; float32 oldImpulse = m_motorImpulse; float32 maxImpulse = data.step.dt * m_maxMotorTorque; m_motorImpulse = b2Clamp(m_motorImpulse + impulse, -maxImpulse, maxImpulse); impulse = m_motorImpulse - oldImpulse; wA -= iA * impulse; wB += iB * impulse; } // Solve point to line constraint { float32 Cdot = b2Dot(m_ay, vB - vA) + m_sBy * wB - m_sAy * wA; float32 impulse = -m_mass * Cdot; m_impulse += impulse; b2Vec2 P = impulse * m_ay; float32 LA = impulse * m_sAy; float32 LB = impulse * m_sBy; vA -= mA * P; wA -= iA * LA; vB += mB * P; wB += iB * LB; } data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; } bool b2WheelJoint::SolvePositionConstraints(const b2SolverData& data) { b2Vec2 cA = data.positions[m_indexA].c; float32 aA = data.positions[m_indexA].a; b2Vec2 cB = data.positions[m_indexB].c; float32 aB = data.positions[m_indexB].a; b2Rot qA(aA), qB(aB); b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA); b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB); b2Vec2 d = (cB - cA) + rB - rA; b2Vec2 ay = b2Mul(qA, m_localYAxisA); float32 sAy = b2Cross(d + rA, ay); float32 sBy = b2Cross(rB, ay); float32 C = b2Dot(d, ay); float32 k = m_invMassA + m_invMassB + m_invIA * m_sAy * m_sAy + m_invIB * m_sBy * m_sBy; float32 impulse; if (k != 0.0f) { impulse = - C / k; } else { impulse = 0.0f; } b2Vec2 P = impulse * ay; float32 LA = impulse * sAy; float32 LB = impulse * sBy; cA -= m_invMassA * P; aA -= m_invIA * LA; cB += m_invMassB * P; aB += m_invIB * LB; data.positions[m_indexA].c = cA; data.positions[m_indexA].a = aA; data.positions[m_indexB].c = cB; data.positions[m_indexB].a = aB; return b2Abs(C) <= b2_linearSlop; } b2Vec2 b2WheelJoint::GetAnchorA() const { return m_bodyA->GetWorldPoint(m_localAnchorA); } b2Vec2 b2WheelJoint::GetAnchorB() const { return m_bodyB->GetWorldPoint(m_localAnchorB); } b2Vec2 b2WheelJoint::GetReactionForce(float32 inv_dt) const { return inv_dt * (m_impulse * m_ay + m_springImpulse * m_ax); } float32 b2WheelJoint::GetReactionTorque(float32 inv_dt) const { return inv_dt * m_motorImpulse; } float32 b2WheelJoint::GetJointTranslation() const { b2Body* bA = m_bodyA; b2Body* bB = m_bodyB; b2Vec2 pA = bA->GetWorldPoint(m_localAnchorA); b2Vec2 pB = bB->GetWorldPoint(m_localAnchorB); b2Vec2 d = pB - pA; b2Vec2 axis = bA->GetWorldVector(m_localXAxisA); float32 translation = b2Dot(d, axis); return translation; } float32 b2WheelJoint::GetJointSpeed() const { float32 wA = m_bodyA->m_angularVelocity; float32 wB = m_bodyB->m_angularVelocity; return wB - wA; } bool b2WheelJoint::IsMotorEnabled() const { return m_enableMotor; } void b2WheelJoint::EnableMotor(bool flag) { m_bodyA->SetAwake(true); m_bodyB->SetAwake(true); m_enableMotor = flag; } void b2WheelJoint::SetMotorSpeed(float32 speed) { m_bodyA->SetAwake(true); m_bodyB->SetAwake(true); m_motorSpeed = speed; } void b2WheelJoint::SetMaxMotorTorque(float32 torque) { m_bodyA->SetAwake(true); m_bodyB->SetAwake(true); m_maxMotorTorque = torque; } float32 b2WheelJoint::GetMotorTorque(float32 inv_dt) const { return inv_dt * m_motorImpulse; } void b2WheelJoint::Dump() { int32 indexA = m_bodyA->m_islandIndex; int32 indexB = m_bodyB->m_islandIndex; b2Log(" b2WheelJointDef jd;\n"); b2Log(" jd.bodyA = bodies[%d];\n", indexA); b2Log(" jd.bodyB = bodies[%d];\n", indexB); b2Log(" jd.collideConnected = bool(%d);\n", m_collideConnected); b2Log(" jd.localAnchorA.Set(%.15lef, %.15lef);\n", m_localAnchorA.x, m_localAnchorA.y); b2Log(" jd.localAnchorB.Set(%.15lef, %.15lef);\n", m_localAnchorB.x, m_localAnchorB.y); b2Log(" jd.localAxisA.Set(%.15lef, %.15lef);\n", m_localXAxisA.x, m_localXAxisA.y); b2Log(" jd.enableMotor = bool(%d);\n", m_enableMotor); b2Log(" jd.motorSpeed = %.15lef;\n", m_motorSpeed); b2Log(" jd.maxMotorTorque = %.15lef;\n", m_maxMotorTorque); b2Log(" jd.frequencyHz = %.15lef;\n", m_frequencyHz); b2Log(" jd.dampingRatio = %.15lef;\n", m_dampingRatio); b2Log(" joints[%d] = m_world->CreateJoint(&jd);\n", m_index); } ================================================ FILE: app/src/main/cpp/b2World.cpp ================================================ /* * Copyright (c) 2006-2011 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Dynamics/b2World.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Body.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Fixture.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Island.h" #include "isEngine/ext_lib/Box2D/Dynamics/Joints/b2PulleyJoint.h" #include "isEngine/ext_lib/Box2D/Dynamics/Contacts/b2Contact.h" #include "isEngine/ext_lib/Box2D/Dynamics/Contacts/b2ContactSolver.h" #include "isEngine/ext_lib/Box2D/Collision/b2Collision.h" #include "isEngine/ext_lib/Box2D/Collision/b2BroadPhase.h" #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2CircleShape.h" #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2EdgeShape.h" #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2ChainShape.h" #include "isEngine/ext_lib/Box2D/Collision/Shapes/b2PolygonShape.h" #include "isEngine/ext_lib/Box2D/Collision/b2TimeOfImpact.h" #include "isEngine/ext_lib/Box2D/Common/b2Draw.h" #include "isEngine/ext_lib/Box2D/Common/b2Timer.h" #include b2World::b2World(const b2Vec2& gravity) { m_destructionListener = NULL; m_debugDraw = NULL; m_bodyList = NULL; m_jointList = NULL; m_bodyCount = 0; m_jointCount = 0; m_warmStarting = true; m_continuousPhysics = true; m_subStepping = false; m_stepComplete = true; m_allowSleep = true; m_gravity = gravity; m_flags = e_clearForces; m_inv_dt0 = 0.0f; m_contactManager.m_allocator = &m_blockAllocator; memset(&m_profile, 0, sizeof(b2Profile)); } b2World::~b2World() { // Some shapes allocate using b2Alloc. b2Body* b = m_bodyList; while (b) { b2Body* bNext = b->m_next; b2Fixture* f = b->m_fixtureList; while (f) { b2Fixture* fNext = f->m_next; f->m_proxyCount = 0; f->Destroy(&m_blockAllocator); f = fNext; } b = bNext; } } void b2World::SetDestructionListener(b2DestructionListener* listener) { m_destructionListener = listener; } void b2World::SetContactFilter(b2ContactFilter* filter) { m_contactManager.m_contactFilter = filter; } void b2World::SetContactListener(b2ContactListener* listener) { m_contactManager.m_contactListener = listener; } void b2World::SetDebugDraw(b2Draw* debugDraw) { m_debugDraw = debugDraw; } b2Body* b2World::CreateBody(const b2BodyDef* def) { b2Assert(IsLocked() == false); if (IsLocked()) { return NULL; } void* mem = m_blockAllocator.Allocate(sizeof(b2Body)); b2Body* b = new (mem) b2Body(def, this); // Add to world doubly linked list. b->m_prev = NULL; b->m_next = m_bodyList; if (m_bodyList) { m_bodyList->m_prev = b; } m_bodyList = b; ++m_bodyCount; return b; } void b2World::DestroyBody(b2Body* b) { b2Assert(m_bodyCount > 0); b2Assert(IsLocked() == false); if (IsLocked()) { return; } // Delete the attached joints. b2JointEdge* je = b->m_jointList; while (je) { b2JointEdge* je0 = je; je = je->next; if (m_destructionListener) { m_destructionListener->SayGoodbye(je0->joint); } DestroyJoint(je0->joint); b->m_jointList = je; } b->m_jointList = NULL; // Delete the attached contacts. b2ContactEdge* ce = b->m_contactList; while (ce) { b2ContactEdge* ce0 = ce; ce = ce->next; m_contactManager.Destroy(ce0->contact); } b->m_contactList = NULL; // Delete the attached fixtures. This destroys broad-phase proxies. b2Fixture* f = b->m_fixtureList; while (f) { b2Fixture* f0 = f; f = f->m_next; if (m_destructionListener) { m_destructionListener->SayGoodbye(f0); } f0->DestroyProxies(&m_contactManager.m_broadPhase); f0->Destroy(&m_blockAllocator); f0->~b2Fixture(); m_blockAllocator.Free(f0, sizeof(b2Fixture)); b->m_fixtureList = f; b->m_fixtureCount -= 1; } b->m_fixtureList = NULL; b->m_fixtureCount = 0; // Remove world body list. if (b->m_prev) { b->m_prev->m_next = b->m_next; } if (b->m_next) { b->m_next->m_prev = b->m_prev; } if (b == m_bodyList) { m_bodyList = b->m_next; } --m_bodyCount; b->~b2Body(); m_blockAllocator.Free(b, sizeof(b2Body)); } b2Joint* b2World::CreateJoint(const b2JointDef* def) { b2Assert(IsLocked() == false); if (IsLocked()) { return NULL; } b2Joint* j = b2Joint::Create(def, &m_blockAllocator); // Connect to the world list. j->m_prev = NULL; j->m_next = m_jointList; if (m_jointList) { m_jointList->m_prev = j; } m_jointList = j; ++m_jointCount; // Connect to the bodies' doubly linked lists. j->m_edgeA.joint = j; j->m_edgeA.other = j->m_bodyB; j->m_edgeA.prev = NULL; j->m_edgeA.next = j->m_bodyA->m_jointList; if (j->m_bodyA->m_jointList) j->m_bodyA->m_jointList->prev = &j->m_edgeA; j->m_bodyA->m_jointList = &j->m_edgeA; j->m_edgeB.joint = j; j->m_edgeB.other = j->m_bodyA; j->m_edgeB.prev = NULL; j->m_edgeB.next = j->m_bodyB->m_jointList; if (j->m_bodyB->m_jointList) j->m_bodyB->m_jointList->prev = &j->m_edgeB; j->m_bodyB->m_jointList = &j->m_edgeB; b2Body* bodyA = def->bodyA; b2Body* bodyB = def->bodyB; // If the joint prevents collisions, then flag any contacts for filtering. if (def->collideConnected == false) { b2ContactEdge* edge = bodyB->GetContactList(); while (edge) { if (edge->other == bodyA) { // Flag the contact for filtering at the next time step (where either // body is awake). edge->contact->FlagForFiltering(); } edge = edge->next; } } // Note: creating a joint doesn't wake the bodies. return j; } void b2World::DestroyJoint(b2Joint* j) { b2Assert(IsLocked() == false); if (IsLocked()) { return; } bool collideConnected = j->m_collideConnected; // Remove from the doubly linked list. if (j->m_prev) { j->m_prev->m_next = j->m_next; } if (j->m_next) { j->m_next->m_prev = j->m_prev; } if (j == m_jointList) { m_jointList = j->m_next; } // Disconnect from island graph. b2Body* bodyA = j->m_bodyA; b2Body* bodyB = j->m_bodyB; // Wake up connected bodies. bodyA->SetAwake(true); bodyB->SetAwake(true); // Remove from body 1. if (j->m_edgeA.prev) { j->m_edgeA.prev->next = j->m_edgeA.next; } if (j->m_edgeA.next) { j->m_edgeA.next->prev = j->m_edgeA.prev; } if (&j->m_edgeA == bodyA->m_jointList) { bodyA->m_jointList = j->m_edgeA.next; } j->m_edgeA.prev = NULL; j->m_edgeA.next = NULL; // Remove from body 2 if (j->m_edgeB.prev) { j->m_edgeB.prev->next = j->m_edgeB.next; } if (j->m_edgeB.next) { j->m_edgeB.next->prev = j->m_edgeB.prev; } if (&j->m_edgeB == bodyB->m_jointList) { bodyB->m_jointList = j->m_edgeB.next; } j->m_edgeB.prev = NULL; j->m_edgeB.next = NULL; b2Joint::Destroy(j, &m_blockAllocator); b2Assert(m_jointCount > 0); --m_jointCount; // If the joint prevents collisions, then flag any contacts for filtering. if (collideConnected == false) { b2ContactEdge* edge = bodyB->GetContactList(); while (edge) { if (edge->other == bodyA) { // Flag the contact for filtering at the next time step (where either // body is awake). edge->contact->FlagForFiltering(); } edge = edge->next; } } } // void b2World::SetAllowSleeping(bool flag) { if (flag == m_allowSleep) { return; } m_allowSleep = flag; if (m_allowSleep == false) { for (b2Body* b = m_bodyList; b; b = b->m_next) { b->SetAwake(true); } } } // Find islands, integrate and solve constraints, solve position constraints void b2World::Solve(const b2TimeStep& step) { m_profile.solveInit = 0.0f; m_profile.solveVelocity = 0.0f; m_profile.solvePosition = 0.0f; // Size the island for the worst case. b2Island island(m_bodyCount, m_contactManager.m_contactCount, m_jointCount, &m_stackAllocator, m_contactManager.m_contactListener); // Clear all the island flags. for (b2Body* b = m_bodyList; b; b = b->m_next) { b->m_flags &= ~b2Body::e_islandFlag; } for (b2Contact* c = m_contactManager.m_contactList; c; c = c->m_next) { c->m_flags &= ~b2Contact::e_islandFlag; } for (b2Joint* j = m_jointList; j; j = j->m_next) { j->m_islandFlag = false; } // Build and simulate all awake islands. int32 stackSize = m_bodyCount; b2Body** stack = (b2Body**)m_stackAllocator.Allocate(stackSize * sizeof(b2Body*)); for (b2Body* seed = m_bodyList; seed; seed = seed->m_next) { if (seed->m_flags & b2Body::e_islandFlag) { continue; } if (seed->IsAwake() == false || seed->IsActive() == false) { continue; } // The seed can be dynamic or kinematic. if (seed->GetType() == b2_staticBody) { continue; } // Reset island and stack. island.Clear(); int32 stackCount = 0; stack[stackCount++] = seed; seed->m_flags |= b2Body::e_islandFlag; // Perform a depth first search (DFS) on the constraint graph. while (stackCount > 0) { // Grab the next body off the stack and add it to the island. b2Body* b = stack[--stackCount]; b2Assert(b->IsActive() == true); island.Add(b); // Make sure the body is awake. b->SetAwake(true); // To keep islands as small as possible, we don't // propagate islands across static bodies. if (b->GetType() == b2_staticBody) { continue; } // Search all contacts connected to this body. for (b2ContactEdge* ce = b->m_contactList; ce; ce = ce->next) { b2Contact* contact = ce->contact; // Has this contact already been added to an island? if (contact->m_flags & b2Contact::e_islandFlag) { continue; } // Is this contact solid and touching? if (contact->IsEnabled() == false || contact->IsTouching() == false) { continue; } // Skip sensors. bool sensorA = contact->m_fixtureA->m_isSensor; bool sensorB = contact->m_fixtureB->m_isSensor; if (sensorA || sensorB) { continue; } island.Add(contact); contact->m_flags |= b2Contact::e_islandFlag; b2Body* other = ce->other; // Was the other body already added to this island? if (other->m_flags & b2Body::e_islandFlag) { continue; } b2Assert(stackCount < stackSize); stack[stackCount++] = other; other->m_flags |= b2Body::e_islandFlag; } // Search all joints connect to this body. for (b2JointEdge* je = b->m_jointList; je; je = je->next) { if (je->joint->m_islandFlag == true) { continue; } b2Body* other = je->other; // Don't simulate joints connected to inactive bodies. if (other->IsActive() == false) { continue; } island.Add(je->joint); je->joint->m_islandFlag = true; if (other->m_flags & b2Body::e_islandFlag) { continue; } b2Assert(stackCount < stackSize); stack[stackCount++] = other; other->m_flags |= b2Body::e_islandFlag; } } b2Profile profile; island.Solve(&profile, step, m_gravity, m_allowSleep); m_profile.solveInit += profile.solveInit; m_profile.solveVelocity += profile.solveVelocity; m_profile.solvePosition += profile.solvePosition; // Post solve cleanup. for (int32 i = 0; i < island.m_bodyCount; ++i) { // Allow static bodies to participate in other islands. b2Body* b = island.m_bodies[i]; if (b->GetType() == b2_staticBody) { b->m_flags &= ~b2Body::e_islandFlag; } } } m_stackAllocator.Free(stack); { b2Timer timer; // Synchronize fixtures, check for out of range bodies. for (b2Body* b = m_bodyList; b; b = b->GetNext()) { // If a body was not in an island then it did not move. if ((b->m_flags & b2Body::e_islandFlag) == 0) { continue; } if (b->GetType() == b2_staticBody) { continue; } // Update fixtures (for broad-phase). b->SynchronizeFixtures(); } // Look for new contacts. m_contactManager.FindNewContacts(); m_profile.broadphase = timer.GetMilliseconds(); } } // Find TOI contacts and solve them. void b2World::SolveTOI(const b2TimeStep& step) { b2Island island(2 * b2_maxTOIContacts, b2_maxTOIContacts, 0, &m_stackAllocator, m_contactManager.m_contactListener); if (m_stepComplete) { for (b2Body* b = m_bodyList; b; b = b->m_next) { b->m_flags &= ~b2Body::e_islandFlag; b->m_sweep.alpha0 = 0.0f; } for (b2Contact* c = m_contactManager.m_contactList; c; c = c->m_next) { // Invalidate TOI c->m_flags &= ~(b2Contact::e_toiFlag | b2Contact::e_islandFlag); c->m_toiCount = 0; c->m_toi = 1.0f; } } // Find TOI events and solve them. for (;;) { // Find the first TOI. b2Contact* minContact = NULL; float32 minAlpha = 1.0f; for (b2Contact* c = m_contactManager.m_contactList; c; c = c->m_next) { // Is this contact disabled? if (c->IsEnabled() == false) { continue; } // Prevent excessive sub-stepping. if (c->m_toiCount > b2_maxSubSteps) { continue; } float32 alpha = 1.0f; if (c->m_flags & b2Contact::e_toiFlag) { // This contact has a valid cached TOI. alpha = c->m_toi; } else { b2Fixture* fA = c->GetFixtureA(); b2Fixture* fB = c->GetFixtureB(); // Is there a sensor? if (fA->IsSensor() || fB->IsSensor()) { continue; } b2Body* bA = fA->GetBody(); b2Body* bB = fB->GetBody(); b2BodyType typeA = bA->m_type; b2BodyType typeB = bB->m_type; b2Assert(typeA == b2_dynamicBody || typeB == b2_dynamicBody); bool activeA = bA->IsAwake() && typeA != b2_staticBody; bool activeB = bB->IsAwake() && typeB != b2_staticBody; // Is at least one body active (awake and dynamic or kinematic)? if (activeA == false && activeB == false) { continue; } bool collideA = bA->IsBullet() || typeA != b2_dynamicBody; bool collideB = bB->IsBullet() || typeB != b2_dynamicBody; // Are these two non-bullet dynamic bodies? if (collideA == false && collideB == false) { continue; } // Compute the TOI for this contact. // Put the sweeps onto the same time interval. float32 alpha0 = bA->m_sweep.alpha0; if (bA->m_sweep.alpha0 < bB->m_sweep.alpha0) { alpha0 = bB->m_sweep.alpha0; bA->m_sweep.Advance(alpha0); } else if (bB->m_sweep.alpha0 < bA->m_sweep.alpha0) { alpha0 = bA->m_sweep.alpha0; bB->m_sweep.Advance(alpha0); } b2Assert(alpha0 < 1.0f); int32 indexA = c->GetChildIndexA(); int32 indexB = c->GetChildIndexB(); // Compute the time of impact in interval [0, minTOI] b2TOIInput input; input.proxyA.Set(fA->GetShape(), indexA); input.proxyB.Set(fB->GetShape(), indexB); input.sweepA = bA->m_sweep; input.sweepB = bB->m_sweep; input.tMax = 1.0f; b2TOIOutput output; b2TimeOfImpact(&output, &input); // Beta is the fraction of the remaining portion of the . float32 beta = output.t; if (output.state == b2TOIOutput::e_touching) { alpha = b2Min(alpha0 + (1.0f - alpha0) * beta, 1.0f); } else { alpha = 1.0f; } c->m_toi = alpha; c->m_flags |= b2Contact::e_toiFlag; } if (alpha < minAlpha) { // This is the minimum TOI found so far. minContact = c; minAlpha = alpha; } } if (minContact == NULL || 1.0f - 10.0f * b2_epsilon < minAlpha) { // No more TOI events. Done! m_stepComplete = true; break; } // Advance the bodies to the TOI. b2Fixture* fA = minContact->GetFixtureA(); b2Fixture* fB = minContact->GetFixtureB(); b2Body* bA = fA->GetBody(); b2Body* bB = fB->GetBody(); b2Sweep backup1 = bA->m_sweep; b2Sweep backup2 = bB->m_sweep; bA->Advance(minAlpha); bB->Advance(minAlpha); // The TOI contact likely has some new contact points. minContact->Update(m_contactManager.m_contactListener); minContact->m_flags &= ~b2Contact::e_toiFlag; ++minContact->m_toiCount; // Is the contact solid? if (minContact->IsEnabled() == false || minContact->IsTouching() == false) { // Restore the sweeps. minContact->SetEnabled(false); bA->m_sweep = backup1; bB->m_sweep = backup2; bA->SynchronizeTransform(); bB->SynchronizeTransform(); continue; } bA->SetAwake(true); bB->SetAwake(true); // Build the island island.Clear(); island.Add(bA); island.Add(bB); island.Add(minContact); bA->m_flags |= b2Body::e_islandFlag; bB->m_flags |= b2Body::e_islandFlag; minContact->m_flags |= b2Contact::e_islandFlag; // Get contacts on bodyA and bodyB. b2Body* bodies[2] = {bA, bB}; for (int32 i = 0; i < 2; ++i) { b2Body* body = bodies[i]; if (body->m_type == b2_dynamicBody) { for (b2ContactEdge* ce = body->m_contactList; ce; ce = ce->next) { if (island.m_bodyCount == island.m_bodyCapacity) { break; } if (island.m_contactCount == island.m_contactCapacity) { break; } b2Contact* contact = ce->contact; // Has this contact already been added to the island? if (contact->m_flags & b2Contact::e_islandFlag) { continue; } // Only add static, kinematic, or bullet bodies. b2Body* other = ce->other; if (other->m_type == b2_dynamicBody && body->IsBullet() == false && other->IsBullet() == false) { continue; } // Skip sensors. bool sensorA = contact->m_fixtureA->m_isSensor; bool sensorB = contact->m_fixtureB->m_isSensor; if (sensorA || sensorB) { continue; } // Tentatively advance the body to the TOI. b2Sweep backup = other->m_sweep; if ((other->m_flags & b2Body::e_islandFlag) == 0) { other->Advance(minAlpha); } // Update the contact points contact->Update(m_contactManager.m_contactListener); // Was the contact disabled by the user? if (contact->IsEnabled() == false) { other->m_sweep = backup; other->SynchronizeTransform(); continue; } // Are there contact points? if (contact->IsTouching() == false) { other->m_sweep = backup; other->SynchronizeTransform(); continue; } // Add the contact to the island contact->m_flags |= b2Contact::e_islandFlag; island.Add(contact); // Has the other body already been added to the island? if (other->m_flags & b2Body::e_islandFlag) { continue; } // Add the other body to the island. other->m_flags |= b2Body::e_islandFlag; if (other->m_type != b2_staticBody) { other->SetAwake(true); } island.Add(other); } } } b2TimeStep subStep; subStep.dt = (1.0f - minAlpha) * step.dt; subStep.inv_dt = 1.0f / subStep.dt; subStep.dtRatio = 1.0f; subStep.positionIterations = 20; subStep.velocityIterations = step.velocityIterations; subStep.warmStarting = false; island.SolveTOI(subStep, bA->m_islandIndex, bB->m_islandIndex); // Reset island flags and synchronize broad-phase proxies. for (int32 i = 0; i < island.m_bodyCount; ++i) { b2Body* body = island.m_bodies[i]; body->m_flags &= ~b2Body::e_islandFlag; if (body->m_type != b2_dynamicBody) { continue; } body->SynchronizeFixtures(); // Invalidate all contact TOIs on this displaced body. for (b2ContactEdge* ce = body->m_contactList; ce; ce = ce->next) { ce->contact->m_flags &= ~(b2Contact::e_toiFlag | b2Contact::e_islandFlag); } } // Commit fixture proxy movements to the broad-phase so that new contacts are created. // Also, some contacts can be destroyed. m_contactManager.FindNewContacts(); if (m_subStepping) { m_stepComplete = false; break; } } } void b2World::Step(float32 dt, int32 velocityIterations, int32 positionIterations) { b2Timer stepTimer; // If new fixtures were added, we need to find the new contacts. if (m_flags & e_newFixture) { m_contactManager.FindNewContacts(); m_flags &= ~e_newFixture; } m_flags |= e_locked; b2TimeStep step; step.dt = dt; step.velocityIterations = velocityIterations; step.positionIterations = positionIterations; if (dt > 0.0f) { step.inv_dt = 1.0f / dt; } else { step.inv_dt = 0.0f; } step.dtRatio = m_inv_dt0 * dt; step.warmStarting = m_warmStarting; // Update contacts. This is where some contacts are destroyed. { b2Timer timer; m_contactManager.Collide(); m_profile.collide = timer.GetMilliseconds(); } // Integrate velocities, solve velocity constraints, and integrate positions. if (m_stepComplete && step.dt > 0.0f) { b2Timer timer; Solve(step); m_profile.solve = timer.GetMilliseconds(); } // Handle TOI events. if (m_continuousPhysics && step.dt > 0.0f) { b2Timer timer; SolveTOI(step); m_profile.solveTOI = timer.GetMilliseconds(); } if (step.dt > 0.0f) { m_inv_dt0 = step.inv_dt; } if (m_flags & e_clearForces) { ClearForces(); } m_flags &= ~e_locked; m_profile.step = stepTimer.GetMilliseconds(); } void b2World::ClearForces() { for (b2Body* body = m_bodyList; body; body = body->GetNext()) { body->m_force.SetZero(); body->m_torque = 0.0f; } } struct b2WorldQueryWrapper { bool QueryCallback(int32 proxyId) { b2FixtureProxy* proxy = (b2FixtureProxy*)broadPhase->GetUserData(proxyId); return callback->ReportFixture(proxy->fixture); } const b2BroadPhase* broadPhase; b2QueryCallback* callback; }; void b2World::QueryAABB(b2QueryCallback* callback, const b2AABB& aabb) const { b2WorldQueryWrapper wrapper; wrapper.broadPhase = &m_contactManager.m_broadPhase; wrapper.callback = callback; m_contactManager.m_broadPhase.Query(&wrapper, aabb); } struct b2WorldRayCastWrapper { float32 RayCastCallback(const b2RayCastInput& input, int32 proxyId) { void* userData = broadPhase->GetUserData(proxyId); b2FixtureProxy* proxy = (b2FixtureProxy*)userData; b2Fixture* fixture = proxy->fixture; int32 index = proxy->childIndex; b2RayCastOutput output; bool hit = fixture->RayCast(&output, input, index); if (hit) { float32 fraction = output.fraction; b2Vec2 point = (1.0f - fraction) * input.p1 + fraction * input.p2; return callback->ReportFixture(fixture, point, output.normal, fraction); } return input.maxFraction; } const b2BroadPhase* broadPhase; b2RayCastCallback* callback; }; void b2World::RayCast(b2RayCastCallback* callback, const b2Vec2& point1, const b2Vec2& point2) const { b2WorldRayCastWrapper wrapper; wrapper.broadPhase = &m_contactManager.m_broadPhase; wrapper.callback = callback; b2RayCastInput input; input.maxFraction = 1.0f; input.p1 = point1; input.p2 = point2; m_contactManager.m_broadPhase.RayCast(&wrapper, input); } void b2World::DrawShape(b2Fixture* fixture, const b2Transform& xf, const b2Color& color) { switch (fixture->GetType()) { case b2Shape::e_circle: { b2CircleShape* circle = (b2CircleShape*)fixture->GetShape(); b2Vec2 center = b2Mul(xf, circle->m_p); float32 radius = circle->m_radius; b2Vec2 axis = b2Mul(xf.q, b2Vec2(1.0f, 0.0f)); m_debugDraw->DrawSolidCircle(center, radius, axis, color); } break; case b2Shape::e_edge: { b2EdgeShape* edge = (b2EdgeShape*)fixture->GetShape(); b2Vec2 v1 = b2Mul(xf, edge->m_vertex1); b2Vec2 v2 = b2Mul(xf, edge->m_vertex2); m_debugDraw->DrawSegment(v1, v2, color); } break; case b2Shape::e_chain: { b2ChainShape* chain = (b2ChainShape*)fixture->GetShape(); int32 count = chain->m_count; const b2Vec2* vertices = chain->m_vertices; b2Vec2 v1 = b2Mul(xf, vertices[0]); for (int32 i = 1; i < count; ++i) { b2Vec2 v2 = b2Mul(xf, vertices[i]); m_debugDraw->DrawSegment(v1, v2, color); m_debugDraw->DrawCircle(v1, 0.05f, color); v1 = v2; } } break; case b2Shape::e_polygon: { b2PolygonShape* poly = (b2PolygonShape*)fixture->GetShape(); int32 vertexCount = poly->m_count; b2Assert(vertexCount <= b2_maxPolygonVertices); b2Vec2 vertices[b2_maxPolygonVertices]; for (int32 i = 0; i < vertexCount; ++i) { vertices[i] = b2Mul(xf, poly->m_vertices[i]); } m_debugDraw->DrawSolidPolygon(vertices, vertexCount, color); } break; default: break; } } void b2World::DrawJoint(b2Joint* joint) { b2Body* bodyA = joint->GetBodyA(); b2Body* bodyB = joint->GetBodyB(); const b2Transform& xf1 = bodyA->GetTransform(); const b2Transform& xf2 = bodyB->GetTransform(); b2Vec2 x1 = xf1.p; b2Vec2 x2 = xf2.p; b2Vec2 p1 = joint->GetAnchorA(); b2Vec2 p2 = joint->GetAnchorB(); b2Color color(0.5f, 0.8f, 0.8f); switch (joint->GetType()) { case e_distanceJoint: m_debugDraw->DrawSegment(p1, p2, color); break; case e_pulleyJoint: { b2PulleyJoint* pulley = (b2PulleyJoint*)joint; b2Vec2 s1 = pulley->GetGroundAnchorA(); b2Vec2 s2 = pulley->GetGroundAnchorB(); m_debugDraw->DrawSegment(s1, p1, color); m_debugDraw->DrawSegment(s2, p2, color); m_debugDraw->DrawSegment(s1, s2, color); } break; case e_mouseJoint: // don't draw this break; default: m_debugDraw->DrawSegment(x1, p1, color); m_debugDraw->DrawSegment(p1, p2, color); m_debugDraw->DrawSegment(x2, p2, color); } } void b2World::DrawDebugData() { if (m_debugDraw == NULL) { return; } uint32 flags = m_debugDraw->GetFlags(); if (flags & b2Draw::e_shapeBit) { for (b2Body* b = m_bodyList; b; b = b->GetNext()) { const b2Transform& xf = b->GetTransform(); for (b2Fixture* f = b->GetFixtureList(); f; f = f->GetNext()) { if (b->IsActive() == false) { DrawShape(f, xf, b2Color(0.5f, 0.5f, 0.3f)); } else if (b->GetType() == b2_staticBody) { DrawShape(f, xf, b2Color(0.5f, 0.9f, 0.5f)); } else if (b->GetType() == b2_kinematicBody) { DrawShape(f, xf, b2Color(0.5f, 0.5f, 0.9f)); } else if (b->IsAwake() == false) { DrawShape(f, xf, b2Color(0.6f, 0.6f, 0.6f)); } else { DrawShape(f, xf, b2Color(0.9f, 0.7f, 0.7f)); } } } } if (flags & b2Draw::e_jointBit) { for (b2Joint* j = m_jointList; j; j = j->GetNext()) { DrawJoint(j); } } if (flags & b2Draw::e_pairBit) { b2Color color(0.3f, 0.9f, 0.9f); for (b2Contact* c = m_contactManager.m_contactList; c; c = c->GetNext()) { //b2Fixture* fixtureA = c->GetFixtureA(); //b2Fixture* fixtureB = c->GetFixtureB(); //b2Vec2 cA = fixtureA->GetAABB().GetCenter(); //b2Vec2 cB = fixtureB->GetAABB().GetCenter(); //m_debugDraw->DrawSegment(cA, cB, color); } } if (flags & b2Draw::e_aabbBit) { b2Color color(0.9f, 0.3f, 0.9f); b2BroadPhase* bp = &m_contactManager.m_broadPhase; for (b2Body* b = m_bodyList; b; b = b->GetNext()) { if (b->IsActive() == false) { continue; } for (b2Fixture* f = b->GetFixtureList(); f; f = f->GetNext()) { for (int32 i = 0; i < f->m_proxyCount; ++i) { b2FixtureProxy* proxy = f->m_proxies + i; b2AABB aabb = bp->GetFatAABB(proxy->proxyId); b2Vec2 vs[4]; vs[0].Set(aabb.lowerBound.x, aabb.lowerBound.y); vs[1].Set(aabb.upperBound.x, aabb.lowerBound.y); vs[2].Set(aabb.upperBound.x, aabb.upperBound.y); vs[3].Set(aabb.lowerBound.x, aabb.upperBound.y); m_debugDraw->DrawPolygon(vs, 4, color); } } } } if (flags & b2Draw::e_centerOfMassBit) { for (b2Body* b = m_bodyList; b; b = b->GetNext()) { b2Transform xf = b->GetTransform(); xf.p = b->GetWorldCenter(); m_debugDraw->DrawTransform(xf); } } } int32 b2World::GetProxyCount() const { return m_contactManager.m_broadPhase.GetProxyCount(); } int32 b2World::GetTreeHeight() const { return m_contactManager.m_broadPhase.GetTreeHeight(); } int32 b2World::GetTreeBalance() const { return m_contactManager.m_broadPhase.GetTreeBalance(); } float32 b2World::GetTreeQuality() const { return m_contactManager.m_broadPhase.GetTreeQuality(); } void b2World::ShiftOrigin(const b2Vec2& newOrigin) { b2Assert((m_flags & e_locked) == 0); if ((m_flags & e_locked) == e_locked) { return; } for (b2Body* b = m_bodyList; b; b = b->m_next) { b->m_xf.p -= newOrigin; b->m_sweep.c0 -= newOrigin; b->m_sweep.c -= newOrigin; } for (b2Joint* j = m_jointList; j; j = j->m_next) { j->ShiftOrigin(newOrigin); } m_contactManager.m_broadPhase.ShiftOrigin(newOrigin); } void b2World::Dump() { if ((m_flags & e_locked) == e_locked) { return; } b2Log("b2Vec2 g(%.15lef, %.15lef);\n", m_gravity.x, m_gravity.y); b2Log("m_world->SetGravity(g);\n"); b2Log("b2Body** bodies = (b2Body**)b2Alloc(%d * sizeof(b2Body*));\n", m_bodyCount); b2Log("b2Joint** joints = (b2Joint**)b2Alloc(%d * sizeof(b2Joint*));\n", m_jointCount); int32 i = 0; for (b2Body* b = m_bodyList; b; b = b->m_next) { b->m_islandIndex = i; b->Dump(); ++i; } i = 0; for (b2Joint* j = m_jointList; j; j = j->m_next) { j->m_index = i; ++i; } // First pass on joints, skip gear joints. for (b2Joint* j = m_jointList; j; j = j->m_next) { if (j->m_type == e_gearJoint) { continue; } b2Log("{\n"); j->Dump(); b2Log("}\n"); } // Second pass on joints, only gear joints. for (b2Joint* j = m_jointList; j; j = j->m_next) { if (j->m_type != e_gearJoint) { continue; } b2Log("{\n"); j->Dump(); b2Log("}\n"); } b2Log("b2Free(joints);\n"); b2Log("b2Free(bodies);\n"); b2Log("joints = NULL;\n"); b2Log("bodies = NULL;\n"); } ================================================ FILE: app/src/main/cpp/b2WorldCallbacks.cpp ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/ext_lib/Box2D/Dynamics/b2WorldCallbacks.h" #include "isEngine/ext_lib/Box2D/Dynamics/b2Fixture.h" // Return true if contact calculations should be performed between these two shapes. // If you implement your own collision filter you may want to build from this implementation. bool b2ContactFilter::ShouldCollide(b2Fixture* fixtureA, b2Fixture* fixtureB) { const b2Filter& filterA = fixtureA->GetFilterData(); const b2Filter& filterB = fixtureB->GetFilterData(); if (filterA.groupIndex == filterB.groupIndex && filterA.groupIndex != 0) { return filterA.groupIndex > 0; } bool collide = (filterA.maskBits & filterB.categoryBits) != 0 && (filterA.categoryBits & filterB.maskBits) != 0; return collide; } ================================================ FILE: app/src/main/cpp/basicSFMLmain.cpp ================================================ #include "isEngine/core/GameEngine.h" namespace is { bool GameEngine::basicSFMLmain() { //////////////////////////////////////////////////////////// // WINDOW CREATION //////////////////////////////////////////////////////////// #if defined(__ANDROID__) m_window.create(sf::VideoMode::getDesktopMode(), ""); #else m_window.create(sf::VideoMode(is::GameConfig::WINDOW_WIDTH, is::GameConfig::WINDOW_HEIGHT), is::GameConfig::GAME_NAME, is::getWindowStyle()); // load application icon sf::Image iconTex; if (!iconTex.loadFromFile(is::GameConfig::GUI_DIR + "icon.png")) return false; #if !defined (IS_ENGINE_SDL_2) && !defined(IS_ENGINE_VS) m_window.setIcon(32, 32, iconTex.getPixelsPtr()); #endif #endif // defined setFPS(m_window, is::GameConfig::FPS); // set frames per second (FPS) sf::View m_view(sf::Vector2f(is::GameConfig::VIEW_WIDTH / 2.f, is::GameConfig::VIEW_HEIGHT / 2.f), sf::Vector2f(is::GameConfig::VIEW_WIDTH, is::GameConfig::VIEW_HEIGHT)); m_window.setView(m_view); //////////////////////////////////////////////////////////// // INITIALIZATION //////////////////////////////////////////////////////////// // is::GameConfig::MUSIC_DIR, is::GameConfig::GUI_DIR, is::GameConfig::FONT_DIR // Are variables that return the path of resources located in the "assets" folder // Load music buffer sf::SoundBuffer musicBuffer; // Music is played in the render loop. See line 172 is::loadSFMLSoundBuffer(musicBuffer, is::GameConfig::MUSIC_DIR + "game_music.wav"); sf::Sound music(musicBuffer); music.play(); // Load texture sf::Texture texture; is::loadSFMLTexture(texture, is::GameConfig::GUI_DIR + "icon.png"); // Create Sprite and set Texture sf::Sprite image(texture); is::centerSFMLObj(image); // Allows to center the sprite image.setPosition(is::GameConfig::VIEW_WIDTH / 2.f, is::GameConfig::VIEW_HEIGHT / 2.f); // Load font sf::Font font; is::loadSFMLFont(font, is::GameConfig::FONT_DIR + "font_system.ttf", 16); // When you develop for the Web you must define // the size that the texts will have with this font // Create text and set font sf::Text text; text.setFont(font); text.setString("Hello World !"); is::centerSFMLObj(text); // Allows to center the text text.setPosition(is::GameConfig::VIEW_WIDTH / 2.f, 64.f); bool focus = true; // Doesn't work when you're on the web version //////////////////////////////////////////////////////////// // RENDER LOOP // //////////////////////////////////////////////////////////// // This starts the render loop. // // Don't touch unless you know what you're doing. // #if !defined(IS_ENGINE_HTML_5) // while (m_window.isOpen() // #ifdef __SWITCH__ // && appletMainLoop() // #endif // ) // #else // EM_ASM(console.log("Start successfully!");, 0); // execMainLoop([&] // { // if (emscripten_run_script_int("Module.syncdone") == 1)// #endif // { // //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// // EVENT //////////////////////////////////////////////////////////// sf::Vector2i mousePosition(-1, -1); // Allows to get mouse or touch position // A negative value means that no position has been recorded sf::Event event; while (m_window.pollEvent(event)) { switch (event.type) { case sf::Event::Closed: m_window.close(); break; #if (defined(__ANDROID__) || defined(__SWITCH__)) case sf::Event::TouchBegan: if (event.touch.finger == 0) { mousePosition.x = is::getCursor(m_window, 0).x; mousePosition.y = is::getCursor(m_window, 0).y; is::vibrate(100); } break; #else case sf::Event::MouseButtonPressed: mousePosition.x = is::getCursor(m_window).x; mousePosition.y = is::getCursor(m_window).y; break; #endif // defined case sf::Event::LostFocus: focus = false; //don't draw, if the window is not shown is::showLog("LOST FOCUS!"); break; case sf::Event::GainedFocus: focus = true; //draw if the window is shown is::showLog("GAINED FOCUS!"); break; default: break; } } //////////////////////////////////////////////////////////// // UPDATE OBJECTS //////////////////////////////////////////////////////////// if (mousePosition.x != -1 && mousePosition.y != -1) image.setPosition(mousePosition.x, mousePosition.y); //////////////////////////////////////////////////////////// // DRAW OBJECTS //////////////////////////////////////////////////////////// if (focus) { m_window.clear(sf::Color::Blue); m_window.draw(text); m_window.draw(image); m_window.display(); } } //////////////////////////////////////////////////////////// // Don't touch unless you know what you're doing. // #if defined(IS_ENGINE_HTML_5) // }); // #endif // //////////////////////////////////////////////////////////// return true; } } ================================================ FILE: app/src/main/cpp/isEngine/core/ActivityController.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef ACTIVITYCONTROLLER_H_INCLUDED #define ACTIVITYCONTROLLER_H_INCLUDED #include "../../app_src/activity/GameActivity.h" class ActivityController { public: ActivityController(is::GameSystemExtended &gameSysExt): m_gameSysExt(gameSysExt) { m_gameActivity = std::make_shared(m_gameSysExt); } void update() { m_gameActivity->onUpdate(); } void draw() { m_gameActivity->onDraw(); if (m_gameActivity->m_changeActivity) { m_gameActivity.reset(); m_gameActivity = std::make_shared(m_gameSysExt); } } private: is::GameSystemExtended &m_gameSysExt; std::shared_ptr m_gameActivity; }; #endif // ACTIVITYCONTROLLER_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/core/GameEngine.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef GAMEENGINE_H_INCLUDED #define GAMEENGINE_H_INCLUDED #include "ActivityController.h" #include //////////////////////////////////////////////////////////// // PC version code #if !defined(__ANDROID__) #if !defined(SFML_SYSTEM_LINUX) && !defined(IS_ENGINE_HTML_5) && !defined(IS_ENGINE_LINUX) && !defined(IS_ENGINE_SWITCH) #include #else #include #endif // defined #endif // defined //////////////////////////////////////////////////////////// namespace is { //////////////////////////////////////////////////////////// /// \brief Class to operate the entire game system /// /// It allows the interconnection of different engine components //////////////////////////////////////////////////////////// class GameEngine { private: sf::RenderWindow m_window; is::GameSystemExtended m_gameSysExt; public: GameEngine(); ~GameEngine(); /// Initialize game engine void initEngine(); #if defined(IS_ENGINE_HTML_5) /// Allow to launch main loop void execMainLoop(std::function loop); void execMainLoop(std::function loop); #endif /// Starts the engine rendering loop bool play(); //////////////////////////////////////////////////////////// /// \brief Starts the SFML classic rendering loop /// /// Useful for implementing a custom rendering loop to facilitate /// the integration of other projects with the engine //////////////////////////////////////////////////////////// bool basicSFMLmain(); /// return Render Window sf::RenderWindow& getRenderWindow() {return m_window;} }; } #endif // GAMEENGINE_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Box2D.h ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef BOX2D_H #define BOX2D_H /** \mainpage Box2D API Documentation \section intro_sec Getting Started For documentation please see http://box2d.org/documentation.html For discussion please visit http://box2d.org/forum */ // These include files constitute the main Box2D API #include "Common/b2Settings.h" #include "Common/b2Draw.h" #include "Common/b2Timer.h" #include "Collision/Shapes/b2CircleShape.h" #include "Collision/Shapes/b2EdgeShape.h" #include "Collision/Shapes/b2ChainShape.h" #include "Collision/Shapes/b2PolygonShape.h" #include "Collision/b2BroadPhase.h" #include "Collision/b2Distance.h" #include "Collision/b2DynamicTree.h" #include "Collision/b2TimeOfImpact.h" #include "Dynamics/b2Body.h" #include "Dynamics/b2Fixture.h" #include "Dynamics/b2WorldCallbacks.h" #include "Dynamics/b2TimeStep.h" #include "Dynamics/b2World.h" #include "Dynamics/Contacts/b2Contact.h" #include "Dynamics/Joints/b2DistanceJoint.h" #include "Dynamics/Joints/b2FrictionJoint.h" #include "Dynamics/Joints/b2GearJoint.h" #include "Dynamics/Joints/b2MotorJoint.h" #include "Dynamics/Joints/b2MouseJoint.h" #include "Dynamics/Joints/b2PrismaticJoint.h" #include "Dynamics/Joints/b2PulleyJoint.h" #include "Dynamics/Joints/b2RevoluteJoint.h" #include "Dynamics/Joints/b2RopeJoint.h" #include "Dynamics/Joints/b2WeldJoint.h" #include "Dynamics/Joints/b2WheelJoint.h" #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Collision/Shapes/b2ChainShape.h ================================================ /* * Copyright (c) 2006-2010 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_CHAIN_SHAPE_H #define B2_CHAIN_SHAPE_H #include "b2Shape.h" class b2EdgeShape; /// A chain shape is a free form sequence of line segments. /// The chain has two-sided collision, so you can use inside and outside collision. /// Therefore, you may use any winding order. /// Since there may be many vertices, they are allocated using b2Alloc. /// Connectivity information is used to create smooth collisions. /// WARNING: The chain will not collide properly if there are self-intersections. class b2ChainShape : public b2Shape { public: b2ChainShape(); /// The destructor frees the vertices using b2Free. ~b2ChainShape(); /// Create a loop. This automatically adjusts connectivity. /// @param vertices an array of vertices, these are copied /// @param count the vertex count void CreateLoop(const b2Vec2* vertices, int32 count); /// Create a chain with isolated end vertices. /// @param vertices an array of vertices, these are copied /// @param count the vertex count void CreateChain(const b2Vec2* vertices, int32 count); /// Establish connectivity to a vertex that precedes the first vertex. /// Don't call this for loops. void SetPrevVertex(const b2Vec2& prevVertex); /// Establish connectivity to a vertex that follows the last vertex. /// Don't call this for loops. void SetNextVertex(const b2Vec2& nextVertex); /// Implement b2Shape. Vertices are cloned using b2Alloc. b2Shape* Clone(b2BlockAllocator* allocator) const; /// @see b2Shape::GetChildCount int32 GetChildCount() const; /// Get a child edge. void GetChildEdge(b2EdgeShape* edge, int32 index) const; /// This always return false. /// @see b2Shape::TestPoint bool TestPoint(const b2Transform& transform, const b2Vec2& p) const; /// Implement b2Shape. bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input, const b2Transform& transform, int32 childIndex) const; /// @see b2Shape::ComputeAABB void ComputeAABB(b2AABB* aabb, const b2Transform& transform, int32 childIndex) const; /// Chains have zero mass. /// @see b2Shape::ComputeMass void ComputeMass(b2MassData* massData, float32 density) const; /// The vertices. Owned by this class. b2Vec2* m_vertices; /// The vertex count. int32 m_count; b2Vec2 m_prevVertex, m_nextVertex; bool m_hasPrevVertex, m_hasNextVertex; }; inline b2ChainShape::b2ChainShape() { m_type = e_chain; m_radius = b2_polygonRadius; m_vertices = NULL; m_count = 0; m_hasPrevVertex = false; m_hasNextVertex = false; } #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Collision/Shapes/b2CircleShape.h ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_CIRCLE_SHAPE_H #define B2_CIRCLE_SHAPE_H #include "b2Shape.h" /// A circle shape. class b2CircleShape : public b2Shape { public: b2CircleShape(); /// Implement b2Shape. b2Shape* Clone(b2BlockAllocator* allocator) const; /// @see b2Shape::GetChildCount int32 GetChildCount() const; /// Implement b2Shape. bool TestPoint(const b2Transform& transform, const b2Vec2& p) const; /// Implement b2Shape. bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input, const b2Transform& transform, int32 childIndex) const; /// @see b2Shape::ComputeAABB void ComputeAABB(b2AABB* aabb, const b2Transform& transform, int32 childIndex) const; /// @see b2Shape::ComputeMass void ComputeMass(b2MassData* massData, float32 density) const; /// Get the supporting vertex index in the given direction. int32 GetSupport(const b2Vec2& d) const; /// Get the supporting vertex in the given direction. const b2Vec2& GetSupportVertex(const b2Vec2& d) const; /// Get the vertex count. int32 GetVertexCount() const { return 1; } /// Get a vertex by index. Used by b2Distance. const b2Vec2& GetVertex(int32 index) const; /// Position b2Vec2 m_p; }; inline b2CircleShape::b2CircleShape() { m_type = e_circle; m_radius = 0.0f; m_p.SetZero(); } inline int32 b2CircleShape::GetSupport(const b2Vec2 &d) const { B2_NOT_USED(d); return 0; } inline const b2Vec2& b2CircleShape::GetSupportVertex(const b2Vec2 &d) const { B2_NOT_USED(d); return m_p; } inline const b2Vec2& b2CircleShape::GetVertex(int32 index) const { B2_NOT_USED(index); b2Assert(index == 0); return m_p; } #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Collision/Shapes/b2EdgeShape.h ================================================ /* * Copyright (c) 2006-2010 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_EDGE_SHAPE_H #define B2_EDGE_SHAPE_H #include "b2Shape.h" /// A line segment (edge) shape. These can be connected in chains or loops /// to other edge shapes. The connectivity information is used to ensure /// correct contact normals. class b2EdgeShape : public b2Shape { public: b2EdgeShape(); /// Set this as an isolated edge. void Set(const b2Vec2& v1, const b2Vec2& v2); /// Implement b2Shape. b2Shape* Clone(b2BlockAllocator* allocator) const; /// @see b2Shape::GetChildCount int32 GetChildCount() const; /// @see b2Shape::TestPoint bool TestPoint(const b2Transform& transform, const b2Vec2& p) const; /// Implement b2Shape. bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input, const b2Transform& transform, int32 childIndex) const; /// @see b2Shape::ComputeAABB void ComputeAABB(b2AABB* aabb, const b2Transform& transform, int32 childIndex) const; /// @see b2Shape::ComputeMass void ComputeMass(b2MassData* massData, float32 density) const; /// These are the edge vertices b2Vec2 m_vertex1, m_vertex2; /// Optional adjacent vertices. These are used for smooth collision. b2Vec2 m_vertex0, m_vertex3; bool m_hasVertex0, m_hasVertex3; }; inline b2EdgeShape::b2EdgeShape() { m_type = e_edge; m_radius = b2_polygonRadius; m_vertex0.x = 0.0f; m_vertex0.y = 0.0f; m_vertex3.x = 0.0f; m_vertex3.y = 0.0f; m_hasVertex0 = false; m_hasVertex3 = false; } #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Collision/Shapes/b2PolygonShape.h ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_POLYGON_SHAPE_H #define B2_POLYGON_SHAPE_H #include "b2Shape.h" /// A convex polygon. It is assumed that the interior of the polygon is to /// the left of each edge. /// Polygons have a maximum number of vertices equal to b2_maxPolygonVertices. /// In most cases you should not need many vertices for a convex polygon. class b2PolygonShape : public b2Shape { public: b2PolygonShape(); /// Implement b2Shape. b2Shape* Clone(b2BlockAllocator* allocator) const; /// @see b2Shape::GetChildCount int32 GetChildCount() const; /// Create a convex hull from the given array of local points. /// The count must be in the range [3, b2_maxPolygonVertices]. /// @warning the points may be re-ordered, even if they form a convex polygon /// @warning collinear points are handled but not removed. Collinear points /// may lead to poor stacking behavior. void Set(const b2Vec2* points, int32 count); /// Build vertices to represent an axis-aligned box centered on the local origin. /// @param hx the half-width. /// @param hy the half-height. void SetAsBox(float32 hx, float32 hy); /// Build vertices to represent an oriented box. /// @param hx the half-width. /// @param hy the half-height. /// @param center the center of the box in local coordinates. /// @param angle the rotation of the box in local coordinates. void SetAsBox(float32 hx, float32 hy, const b2Vec2& center, float32 angle); /// @see b2Shape::TestPoint bool TestPoint(const b2Transform& transform, const b2Vec2& p) const; /// Implement b2Shape. bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input, const b2Transform& transform, int32 childIndex) const; /// @see b2Shape::ComputeAABB void ComputeAABB(b2AABB* aabb, const b2Transform& transform, int32 childIndex) const; /// @see b2Shape::ComputeMass void ComputeMass(b2MassData* massData, float32 density) const; /// Get the vertex count. int32 GetVertexCount() const { return m_count; } /// Get a vertex by index. const b2Vec2& GetVertex(int32 index) const; /// Validate convexity. This is a very time consuming operation. /// @returns true if valid bool Validate() const; b2Vec2 m_centroid; b2Vec2 m_vertices[b2_maxPolygonVertices]; b2Vec2 m_normals[b2_maxPolygonVertices]; int32 m_count; }; inline b2PolygonShape::b2PolygonShape() { m_type = e_polygon; m_radius = b2_polygonRadius; m_count = 0; m_centroid.SetZero(); } inline const b2Vec2& b2PolygonShape::GetVertex(int32 index) const { b2Assert(0 <= index && index < m_count); return m_vertices[index]; } #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Collision/Shapes/b2Shape.h ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_SHAPE_H #define B2_SHAPE_H #include "../../Common/b2BlockAllocator.h" #include "../../Common/b2Math.h" #include "../b2Collision.h" /// This holds the mass data computed for a shape. struct b2MassData { /// The mass of the shape, usually in kilograms. float32 mass; /// The position of the shape's centroid relative to the shape's origin. b2Vec2 center; /// The rotational inertia of the shape about the local origin. float32 I; }; /// A shape is used for collision detection. You can create a shape however you like. /// Shapes used for simulation in b2World are created automatically when a b2Fixture /// is created. Shapes may encapsulate a one or more child shapes. class b2Shape { public: enum Type { e_circle = 0, e_edge = 1, e_polygon = 2, e_chain = 3, e_typeCount = 4 }; virtual ~b2Shape() {} /// Clone the concrete shape using the provided allocator. virtual b2Shape* Clone(b2BlockAllocator* allocator) const = 0; /// Get the type of this shape. You can use this to down cast to the concrete shape. /// @return the shape type. Type GetType() const; /// Get the number of child primitives. virtual int32 GetChildCount() const = 0; /// Test a point for containment in this shape. This only works for convex shapes. /// @param xf the shape world transform. /// @param p a point in world coordinates. virtual bool TestPoint(const b2Transform& xf, const b2Vec2& p) const = 0; /// Cast a ray against a child shape. /// @param output the ray-cast results. /// @param input the ray-cast input parameters. /// @param transform the transform to be applied to the shape. /// @param childIndex the child shape index virtual bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input, const b2Transform& transform, int32 childIndex) const = 0; /// Given a transform, compute the associated axis aligned bounding box for a child shape. /// @param aabb returns the axis aligned box. /// @param xf the world transform of the shape. /// @param childIndex the child shape virtual void ComputeAABB(b2AABB* aabb, const b2Transform& xf, int32 childIndex) const = 0; /// Compute the mass properties of this shape using its dimensions and density. /// The inertia tensor is computed about the local origin. /// @param massData returns the mass data for this shape. /// @param density the density in kilograms per meter squared. virtual void ComputeMass(b2MassData* massData, float32 density) const = 0; Type m_type; float32 m_radius; }; inline b2Shape::Type b2Shape::GetType() const { return m_type; } #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Collision/b2BroadPhase.h ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_BROAD_PHASE_H #define B2_BROAD_PHASE_H #include "../Common/b2Settings.h" #include "b2Collision.h" #include "b2DynamicTree.h" #include struct b2Pair { int32 proxyIdA; int32 proxyIdB; }; /// The broad-phase is used for computing pairs and performing volume queries and ray casts. /// This broad-phase does not persist pairs. Instead, this reports potentially new pairs. /// It is up to the client to consume the new pairs and to track subsequent overlap. class b2BroadPhase { public: enum { e_nullProxy = -1 }; b2BroadPhase(); ~b2BroadPhase(); /// Create a proxy with an initial AABB. Pairs are not reported until /// UpdatePairs is called. int32 CreateProxy(const b2AABB& aabb, void* userData); /// Destroy a proxy. It is up to the client to remove any pairs. void DestroyProxy(int32 proxyId); /// Call MoveProxy as many times as you like, then when you are done /// call UpdatePairs to finalized the proxy pairs (for your time step). void MoveProxy(int32 proxyId, const b2AABB& aabb, const b2Vec2& displacement); /// Call to trigger a re-processing of it's pairs on the next call to UpdatePairs. void TouchProxy(int32 proxyId); /// Get the fat AABB for a proxy. const b2AABB& GetFatAABB(int32 proxyId) const; /// Get user data from a proxy. Returns NULL if the id is invalid. void* GetUserData(int32 proxyId) const; /// Test overlap of fat AABBs. bool TestOverlap(int32 proxyIdA, int32 proxyIdB) const; /// Get the number of proxies. int32 GetProxyCount() const; /// Update the pairs. This results in pair callbacks. This can only add pairs. template void UpdatePairs(T* callback); /// Query an AABB for overlapping proxies. The callback class /// is called for each proxy that overlaps the supplied AABB. template void Query(T* callback, const b2AABB& aabb) const; /// Ray-cast against the proxies in the tree. This relies on the callback /// to perform a exact ray-cast in the case were the proxy contains a shape. /// The callback also performs the any collision filtering. This has performance /// roughly equal to k * log(n), where k is the number of collisions and n is the /// number of proxies in the tree. /// @param input the ray-cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1). /// @param callback a callback class that is called for each proxy that is hit by the ray. template void RayCast(T* callback, const b2RayCastInput& input) const; /// Get the height of the embedded tree. int32 GetTreeHeight() const; /// Get the balance of the embedded tree. int32 GetTreeBalance() const; /// Get the quality metric of the embedded tree. float32 GetTreeQuality() const; /// Shift the world origin. Useful for large worlds. /// The shift formula is: position -= newOrigin /// @param newOrigin the new origin with respect to the old origin void ShiftOrigin(const b2Vec2& newOrigin); private: friend class b2DynamicTree; void BufferMove(int32 proxyId); void UnBufferMove(int32 proxyId); bool QueryCallback(int32 proxyId); b2DynamicTree m_tree; int32 m_proxyCount; int32* m_moveBuffer; int32 m_moveCapacity; int32 m_moveCount; b2Pair* m_pairBuffer; int32 m_pairCapacity; int32 m_pairCount; int32 m_queryProxyId; }; /// This is used to sort pairs. inline bool b2PairLessThan(const b2Pair& pair1, const b2Pair& pair2) { if (pair1.proxyIdA < pair2.proxyIdA) { return true; } if (pair1.proxyIdA == pair2.proxyIdA) { return pair1.proxyIdB < pair2.proxyIdB; } return false; } inline void* b2BroadPhase::GetUserData(int32 proxyId) const { return m_tree.GetUserData(proxyId); } inline bool b2BroadPhase::TestOverlap(int32 proxyIdA, int32 proxyIdB) const { const b2AABB& aabbA = m_tree.GetFatAABB(proxyIdA); const b2AABB& aabbB = m_tree.GetFatAABB(proxyIdB); return b2TestOverlap(aabbA, aabbB); } inline const b2AABB& b2BroadPhase::GetFatAABB(int32 proxyId) const { return m_tree.GetFatAABB(proxyId); } inline int32 b2BroadPhase::GetProxyCount() const { return m_proxyCount; } inline int32 b2BroadPhase::GetTreeHeight() const { return m_tree.GetHeight(); } inline int32 b2BroadPhase::GetTreeBalance() const { return m_tree.GetMaxBalance(); } inline float32 b2BroadPhase::GetTreeQuality() const { return m_tree.GetAreaRatio(); } template void b2BroadPhase::UpdatePairs(T* callback) { // Reset pair buffer m_pairCount = 0; // Perform tree queries for all moving proxies. for (int32 i = 0; i < m_moveCount; ++i) { m_queryProxyId = m_moveBuffer[i]; if (m_queryProxyId == e_nullProxy) { continue; } // We have to query the tree with the fat AABB so that // we don't fail to create a pair that may touch later. const b2AABB& fatAABB = m_tree.GetFatAABB(m_queryProxyId); // Query tree, create pairs and add them pair buffer. m_tree.Query(this, fatAABB); } // Reset move buffer m_moveCount = 0; // Sort the pair buffer to expose duplicates. std::sort(m_pairBuffer, m_pairBuffer + m_pairCount, b2PairLessThan); // Send the pairs back to the client. int32 i = 0; while (i < m_pairCount) { b2Pair* primaryPair = m_pairBuffer + i; void* userDataA = m_tree.GetUserData(primaryPair->proxyIdA); void* userDataB = m_tree.GetUserData(primaryPair->proxyIdB); callback->AddPair(userDataA, userDataB); ++i; // Skip any duplicate pairs. while (i < m_pairCount) { b2Pair* pair = m_pairBuffer + i; if (pair->proxyIdA != primaryPair->proxyIdA || pair->proxyIdB != primaryPair->proxyIdB) { break; } ++i; } } // Try to keep the tree balanced. //m_tree.Rebalance(4); } template inline void b2BroadPhase::Query(T* callback, const b2AABB& aabb) const { m_tree.Query(callback, aabb); } template inline void b2BroadPhase::RayCast(T* callback, const b2RayCastInput& input) const { m_tree.RayCast(callback, input); } inline void b2BroadPhase::ShiftOrigin(const b2Vec2& newOrigin) { m_tree.ShiftOrigin(newOrigin); } #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Collision/b2Collision.h ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_COLLISION_H #define B2_COLLISION_H #include "../Common/b2Math.h" #include /// @file /// Structures and functions used for computing contact points, distance /// queries, and TOI queries. class b2Shape; class b2CircleShape; class b2EdgeShape; class b2PolygonShape; const uint8 b2_nullFeature = UCHAR_MAX; /// The features that intersect to form the contact point /// This must be 4 bytes or less. struct b2ContactFeature { enum Type { e_vertex = 0, e_face = 1 }; uint8 indexA; ///< Feature index on shapeA uint8 indexB; ///< Feature index on shapeB uint8 typeA; ///< The feature type on shapeA uint8 typeB; ///< The feature type on shapeB }; /// Contact ids to facilitate warm starting. union b2ContactID { b2ContactFeature cf; uint32 key; ///< Used to quickly compare contact ids. }; /// A manifold point is a contact point belonging to a contact /// manifold. It holds details related to the geometry and dynamics /// of the contact points. /// The local point usage depends on the manifold type: /// -e_circles: the local center of circleB /// -e_faceA: the local center of cirlceB or the clip point of polygonB /// -e_faceB: the clip point of polygonA /// This structure is stored across time steps, so we keep it small. /// Note: the impulses are used for internal caching and may not /// provide reliable contact forces, especially for high speed collisions. struct b2ManifoldPoint { b2Vec2 localPoint; ///< usage depends on manifold type float32 normalImpulse; ///< the non-penetration impulse float32 tangentImpulse; ///< the friction impulse b2ContactID id; ///< uniquely identifies a contact point between two shapes }; /// A manifold for two touching convex shapes. /// Box2D supports multiple types of contact: /// - clip point versus plane with radius /// - point versus point with radius (circles) /// The local point usage depends on the manifold type: /// -e_circles: the local center of circleA /// -e_faceA: the center of faceA /// -e_faceB: the center of faceB /// Similarly the local normal usage: /// -e_circles: not used /// -e_faceA: the normal on polygonA /// -e_faceB: the normal on polygonB /// We store contacts in this way so that position correction can /// account for movement, which is critical for continuous physics. /// All contact scenarios must be expressed in one of these types. /// This structure is stored across time steps, so we keep it small. struct b2Manifold { enum Type { e_circles, e_faceA, e_faceB }; b2ManifoldPoint points[b2_maxManifoldPoints]; ///< the points of contact b2Vec2 localNormal; ///< not use for Type::e_points b2Vec2 localPoint; ///< usage depends on manifold type Type type; int32 pointCount; ///< the number of manifold points }; /// This is used to compute the current state of a contact manifold. struct b2WorldManifold { /// Evaluate the manifold with supplied transforms. This assumes /// modest motion from the original state. This does not change the /// point count, impulses, etc. The radii must come from the shapes /// that generated the manifold. void Initialize(const b2Manifold* manifold, const b2Transform& xfA, float32 radiusA, const b2Transform& xfB, float32 radiusB); b2Vec2 normal; ///< world vector pointing from A to B b2Vec2 points[b2_maxManifoldPoints]; ///< world contact point (point of intersection) float32 separations[b2_maxManifoldPoints]; ///< a negative value indicates overlap, in meters }; /// This is used for determining the state of contact points. enum b2PointState { b2_nullState, ///< point does not exist b2_addState, ///< point was added in the update b2_persistState, ///< point persisted across the update b2_removeState ///< point was removed in the update }; /// Compute the point states given two manifolds. The states pertain to the transition from manifold1 /// to manifold2. So state1 is either persist or remove while state2 is either add or persist. void b2GetPointStates(b2PointState state1[b2_maxManifoldPoints], b2PointState state2[b2_maxManifoldPoints], const b2Manifold* manifold1, const b2Manifold* manifold2); /// Used for computing contact manifolds. struct b2ClipVertex { b2Vec2 v; b2ContactID id; }; /// Ray-cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1). struct b2RayCastInput { b2Vec2 p1, p2; float32 maxFraction; }; /// Ray-cast output data. The ray hits at p1 + fraction * (p2 - p1), where p1 and p2 /// come from b2RayCastInput. struct b2RayCastOutput { b2Vec2 normal; float32 fraction; }; /// An axis aligned bounding box. struct b2AABB { /// Verify that the bounds are sorted. bool IsValid() const; /// Get the center of the AABB. b2Vec2 GetCenter() const { return 0.5f * (lowerBound + upperBound); } /// Get the extents of the AABB (half-widths). b2Vec2 GetExtents() const { return 0.5f * (upperBound - lowerBound); } /// Get the perimeter length float32 GetPerimeter() const { float32 wx = upperBound.x - lowerBound.x; float32 wy = upperBound.y - lowerBound.y; return 2.0f * (wx + wy); } /// Combine an AABB into this one. void Combine(const b2AABB& aabb) { lowerBound = b2Min(lowerBound, aabb.lowerBound); upperBound = b2Max(upperBound, aabb.upperBound); } /// Combine two AABBs into this one. void Combine(const b2AABB& aabb1, const b2AABB& aabb2) { lowerBound = b2Min(aabb1.lowerBound, aabb2.lowerBound); upperBound = b2Max(aabb1.upperBound, aabb2.upperBound); } /// Does this aabb contain the provided AABB. bool Contains(const b2AABB& aabb) const { bool result = true; result = result && lowerBound.x <= aabb.lowerBound.x; result = result && lowerBound.y <= aabb.lowerBound.y; result = result && aabb.upperBound.x <= upperBound.x; result = result && aabb.upperBound.y <= upperBound.y; return result; } bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input) const; b2Vec2 lowerBound; ///< the lower vertex b2Vec2 upperBound; ///< the upper vertex }; /// Compute the collision manifold between two circles. void b2CollideCircles(b2Manifold* manifold, const b2CircleShape* circleA, const b2Transform& xfA, const b2CircleShape* circleB, const b2Transform& xfB); /// Compute the collision manifold between a polygon and a circle. void b2CollidePolygonAndCircle(b2Manifold* manifold, const b2PolygonShape* polygonA, const b2Transform& xfA, const b2CircleShape* circleB, const b2Transform& xfB); /// Compute the collision manifold between two polygons. void b2CollidePolygons(b2Manifold* manifold, const b2PolygonShape* polygonA, const b2Transform& xfA, const b2PolygonShape* polygonB, const b2Transform& xfB); /// Compute the collision manifold between an edge and a circle. void b2CollideEdgeAndCircle(b2Manifold* manifold, const b2EdgeShape* polygonA, const b2Transform& xfA, const b2CircleShape* circleB, const b2Transform& xfB); /// Compute the collision manifold between an edge and a circle. void b2CollideEdgeAndPolygon(b2Manifold* manifold, const b2EdgeShape* edgeA, const b2Transform& xfA, const b2PolygonShape* circleB, const b2Transform& xfB); /// Clipping for contact manifolds. int32 b2ClipSegmentToLine(b2ClipVertex vOut[2], const b2ClipVertex vIn[2], const b2Vec2& normal, float32 offset, int32 vertexIndexA); /// Determine if two generic shapes overlap. bool b2TestOverlap( const b2Shape* shapeA, int32 indexA, const b2Shape* shapeB, int32 indexB, const b2Transform& xfA, const b2Transform& xfB); // ---------------- Inline Functions ------------------------------------------ inline bool b2AABB::IsValid() const { b2Vec2 d = upperBound - lowerBound; bool valid = d.x >= 0.0f && d.y >= 0.0f; valid = valid && lowerBound.IsValid() && upperBound.IsValid(); return valid; } inline bool b2TestOverlap(const b2AABB& a, const b2AABB& b) { b2Vec2 d1, d2; d1 = b.lowerBound - a.upperBound; d2 = a.lowerBound - b.upperBound; if (d1.x > 0.0f || d1.y > 0.0f) return false; if (d2.x > 0.0f || d2.y > 0.0f) return false; return true; } #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Collision/b2Distance.h ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_DISTANCE_H #define B2_DISTANCE_H #include "../Common/b2Math.h" class b2Shape; /// A distance proxy is used by the GJK algorithm. /// It encapsulates any shape. struct b2DistanceProxy { b2DistanceProxy() : m_vertices(NULL), m_count(0), m_radius(0.0f) {} /// Initialize the proxy using the given shape. The shape /// must remain in scope while the proxy is in use. void Set(const b2Shape* shape, int32 index); /// Get the supporting vertex index in the given direction. int32 GetSupport(const b2Vec2& d) const; /// Get the supporting vertex in the given direction. const b2Vec2& GetSupportVertex(const b2Vec2& d) const; /// Get the vertex count. int32 GetVertexCount() const; /// Get a vertex by index. Used by b2Distance. const b2Vec2& GetVertex(int32 index) const; b2Vec2 m_buffer[2]; const b2Vec2* m_vertices; int32 m_count; float32 m_radius; }; /// Used to warm start b2Distance. /// Set count to zero on first call. struct b2SimplexCache { float32 metric; ///< length or area uint16 count; uint8 indexA[3]; ///< vertices on shape A uint8 indexB[3]; ///< vertices on shape B }; /// Input for b2Distance. /// You have to option to use the shape radii /// in the computation. Even struct b2DistanceInput { b2DistanceProxy proxyA; b2DistanceProxy proxyB; b2Transform transformA; b2Transform transformB; bool useRadii; }; /// Output for b2Distance. struct b2DistanceOutput { b2Vec2 pointA; ///< closest point on shapeA b2Vec2 pointB; ///< closest point on shapeB float32 distance; int32 iterations; ///< number of GJK iterations used }; /// Compute the closest points between two shapes. Supports any combination of: /// b2CircleShape, b2PolygonShape, b2EdgeShape. The simplex cache is input/output. /// On the first call set b2SimplexCache.count to zero. void b2Distance(b2DistanceOutput* output, b2SimplexCache* cache, const b2DistanceInput* input); ////////////////////////////////////////////////////////////////////////// inline int32 b2DistanceProxy::GetVertexCount() const { return m_count; } inline const b2Vec2& b2DistanceProxy::GetVertex(int32 index) const { b2Assert(0 <= index && index < m_count); return m_vertices[index]; } inline int32 b2DistanceProxy::GetSupport(const b2Vec2& d) const { int32 bestIndex = 0; float32 bestValue = b2Dot(m_vertices[0], d); for (int32 i = 1; i < m_count; ++i) { float32 value = b2Dot(m_vertices[i], d); if (value > bestValue) { bestIndex = i; bestValue = value; } } return bestIndex; } inline const b2Vec2& b2DistanceProxy::GetSupportVertex(const b2Vec2& d) const { int32 bestIndex = 0; float32 bestValue = b2Dot(m_vertices[0], d); for (int32 i = 1; i < m_count; ++i) { float32 value = b2Dot(m_vertices[i], d); if (value > bestValue) { bestIndex = i; bestValue = value; } } return m_vertices[bestIndex]; } #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Collision/b2DynamicTree.h ================================================ /* * Copyright (c) 2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_DYNAMIC_TREE_H #define B2_DYNAMIC_TREE_H #include "b2Collision.h" #include "../Common/b2GrowableStack.h" #define b2_nullNode (-1) /// A node in the dynamic tree. The client does not interact with this directly. struct b2TreeNode { bool IsLeaf() const { return child1 == b2_nullNode; } /// Enlarged AABB b2AABB aabb; void* userData; union { int32 parent; int32 next; }; int32 child1; int32 child2; // leaf = 0, free node = -1 int32 height; }; /// A dynamic AABB tree broad-phase, inspired by Nathanael Presson's btDbvt. /// A dynamic tree arranges data in a binary tree to accelerate /// queries such as volume queries and ray casts. Leafs are proxies /// with an AABB. In the tree we expand the proxy AABB by b2_fatAABBFactor /// so that the proxy AABB is bigger than the client object. This allows the client /// object to move by small amounts without triggering a tree update. /// /// Nodes are pooled and relocatable, so we use node indices rather than pointers. class b2DynamicTree { public: /// Constructing the tree initializes the node pool. b2DynamicTree(); /// Destroy the tree, freeing the node pool. ~b2DynamicTree(); /// Create a proxy. Provide a tight fitting AABB and a userData pointer. int32 CreateProxy(const b2AABB& aabb, void* userData); /// Destroy a proxy. This asserts if the id is invalid. void DestroyProxy(int32 proxyId); /// Move a proxy with a swepted AABB. If the proxy has moved outside of its fattened AABB, /// then the proxy is removed from the tree and re-inserted. Otherwise /// the function returns immediately. /// @return true if the proxy was re-inserted. bool MoveProxy(int32 proxyId, const b2AABB& aabb1, const b2Vec2& displacement); /// Get proxy user data. /// @return the proxy user data or 0 if the id is invalid. void* GetUserData(int32 proxyId) const; /// Get the fat AABB for a proxy. const b2AABB& GetFatAABB(int32 proxyId) const; /// Query an AABB for overlapping proxies. The callback class /// is called for each proxy that overlaps the supplied AABB. template void Query(T* callback, const b2AABB& aabb) const; /// Ray-cast against the proxies in the tree. This relies on the callback /// to perform a exact ray-cast in the case were the proxy contains a shape. /// The callback also performs the any collision filtering. This has performance /// roughly equal to k * log(n), where k is the number of collisions and n is the /// number of proxies in the tree. /// @param input the ray-cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1). /// @param callback a callback class that is called for each proxy that is hit by the ray. template void RayCast(T* callback, const b2RayCastInput& input) const; /// Validate this tree. For testing. void Validate() const; /// Compute the height of the binary tree in O(N) time. Should not be /// called often. int32 GetHeight() const; /// Get the maximum balance of an node in the tree. The balance is the difference /// in height of the two children of a node. int32 GetMaxBalance() const; /// Get the ratio of the sum of the node areas to the root area. float32 GetAreaRatio() const; /// Build an optimal tree. Very expensive. For testing. void RebuildBottomUp(); /// Shift the world origin. Useful for large worlds. /// The shift formula is: position -= newOrigin /// @param newOrigin the new origin with respect to the old origin void ShiftOrigin(const b2Vec2& newOrigin); private: int32 AllocateNode(); void FreeNode(int32 node); void InsertLeaf(int32 node); void RemoveLeaf(int32 node); int32 Balance(int32 index); int32 ComputeHeight() const; int32 ComputeHeight(int32 nodeId) const; void ValidateStructure(int32 index) const; void ValidateMetrics(int32 index) const; int32 m_root; b2TreeNode* m_nodes; int32 m_nodeCount; int32 m_nodeCapacity; int32 m_freeList; /// This is used to incrementally traverse the tree for re-balancing. uint32 m_path; int32 m_insertionCount; }; inline void* b2DynamicTree::GetUserData(int32 proxyId) const { b2Assert(0 <= proxyId && proxyId < m_nodeCapacity); return m_nodes[proxyId].userData; } inline const b2AABB& b2DynamicTree::GetFatAABB(int32 proxyId) const { b2Assert(0 <= proxyId && proxyId < m_nodeCapacity); return m_nodes[proxyId].aabb; } template inline void b2DynamicTree::Query(T* callback, const b2AABB& aabb) const { b2GrowableStack stack; stack.Push(m_root); while (stack.GetCount() > 0) { int32 nodeId = stack.Pop(); if (nodeId == b2_nullNode) { continue; } const b2TreeNode* node = m_nodes + nodeId; if (b2TestOverlap(node->aabb, aabb)) { if (node->IsLeaf()) { bool proceed = callback->QueryCallback(nodeId); if (proceed == false) { return; } } else { stack.Push(node->child1); stack.Push(node->child2); } } } } template inline void b2DynamicTree::RayCast(T* callback, const b2RayCastInput& input) const { b2Vec2 p1 = input.p1; b2Vec2 p2 = input.p2; b2Vec2 r = p2 - p1; b2Assert(r.LengthSquared() > 0.0f); r.Normalize(); // v is perpendicular to the segment. b2Vec2 v = b2Cross(1.0f, r); b2Vec2 abs_v = b2Abs(v); // Separating axis for segment (Gino, p80). // |dot(v, p1 - c)| > dot(|v|, h) float32 maxFraction = input.maxFraction; // Build a bounding box for the segment. b2AABB segmentAABB; { b2Vec2 t = p1 + maxFraction * (p2 - p1); segmentAABB.lowerBound = b2Min(p1, t); segmentAABB.upperBound = b2Max(p1, t); } b2GrowableStack stack; stack.Push(m_root); while (stack.GetCount() > 0) { int32 nodeId = stack.Pop(); if (nodeId == b2_nullNode) { continue; } const b2TreeNode* node = m_nodes + nodeId; if (b2TestOverlap(node->aabb, segmentAABB) == false) { continue; } // Separating axis for segment (Gino, p80). // |dot(v, p1 - c)| > dot(|v|, h) b2Vec2 c = node->aabb.GetCenter(); b2Vec2 h = node->aabb.GetExtents(); float32 separation = b2Abs(b2Dot(v, p1 - c)) - b2Dot(abs_v, h); if (separation > 0.0f) { continue; } if (node->IsLeaf()) { b2RayCastInput subInput; subInput.p1 = input.p1; subInput.p2 = input.p2; subInput.maxFraction = maxFraction; float32 value = callback->RayCastCallback(subInput, nodeId); if (value == 0.0f) { // The client has terminated the ray cast. return; } if (value > 0.0f) { // Update segment bounding box. maxFraction = value; b2Vec2 t = p1 + maxFraction * (p2 - p1); segmentAABB.lowerBound = b2Min(p1, t); segmentAABB.upperBound = b2Max(p1, t); } } else { stack.Push(node->child1); stack.Push(node->child2); } } } #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Collision/b2TimeOfImpact.h ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_TIME_OF_IMPACT_H #define B2_TIME_OF_IMPACT_H #include "../Common/b2Math.h" #include "b2Distance.h" /// Input parameters for b2TimeOfImpact struct b2TOIInput { b2DistanceProxy proxyA; b2DistanceProxy proxyB; b2Sweep sweepA; b2Sweep sweepB; float32 tMax; // defines sweep interval [0, tMax] }; // Output parameters for b2TimeOfImpact. struct b2TOIOutput { enum State { e_unknown, e_failed, e_overlapped, e_touching, e_separated }; State state; float32 t; }; /// Compute the upper bound on time before two shapes penetrate. Time is represented as /// a fraction between [0,tMax]. This uses a swept separating axis and may miss some intermediate, /// non-tunneling collision. If you change the time interval, you should call this function /// again. /// Note: use b2Distance to compute the contact point and normal at the time of impact. void b2TimeOfImpact(b2TOIOutput* output, const b2TOIInput* input); #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Common/b2BlockAllocator.h ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_BLOCK_ALLOCATOR_H #define B2_BLOCK_ALLOCATOR_H #include "b2Settings.h" const int32 b2_chunkSize = 16 * 1024; const int32 b2_maxBlockSize = 640; const int32 b2_blockSizes = 14; const int32 b2_chunkArrayIncrement = 128; struct b2Block; struct b2Chunk; /// This is a small object allocator used for allocating small /// objects that persist for more than one time step. /// See: http://www.codeproject.com/useritems/Small_Block_Allocator.asp class b2BlockAllocator { public: b2BlockAllocator(); ~b2BlockAllocator(); /// Allocate memory. This will use b2Alloc if the size is larger than b2_maxBlockSize. void* Allocate(int32 size); /// Free memory. This will use b2Free if the size is larger than b2_maxBlockSize. void Free(void* p, int32 size); void Clear(); private: b2Chunk* m_chunks; int32 m_chunkCount; int32 m_chunkSpace; b2Block* m_freeLists[b2_blockSizes]; static int32 s_blockSizes[b2_blockSizes]; static uint8 s_blockSizeLookup[b2_maxBlockSize + 1]; static bool s_blockSizeLookupInitialized; }; #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Common/b2Draw.h ================================================ /* * Copyright (c) 2011 Erin Catto http://box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_DRAW_H #define B2_DRAW_H #include "b2Math.h" /// Color for debug drawing. Each value has the range [0,1]. struct b2Color { b2Color() {} b2Color(float32 r, float32 g, float32 b) : r(r), g(g), b(b) {} void Set(float32 ri, float32 gi, float32 bi) { r = ri; g = gi; b = bi; } float32 r, g, b; }; /// Implement and register this class with a b2World to provide debug drawing of physics /// entities in your game. class b2Draw { public: b2Draw(); virtual ~b2Draw() {} enum { e_shapeBit = 0x0001, ///< draw shapes e_jointBit = 0x0002, ///< draw joint connections e_aabbBit = 0x0004, ///< draw axis aligned bounding boxes e_pairBit = 0x0008, ///< draw broad-phase pairs e_centerOfMassBit = 0x0010 ///< draw center of mass frame }; /// Set the drawing flags. void SetFlags(uint32 flags); /// Get the drawing flags. uint32 GetFlags() const; /// Append flags to the current flags. void AppendFlags(uint32 flags); /// Clear flags from the current flags. void ClearFlags(uint32 flags); /// Draw a closed polygon provided in CCW order. virtual void DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) = 0; /// Draw a solid closed polygon provided in CCW order. virtual void DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) = 0; /// Draw a circle. virtual void DrawCircle(const b2Vec2& center, float32 radius, const b2Color& color) = 0; /// Draw a solid circle. virtual void DrawSolidCircle(const b2Vec2& center, float32 radius, const b2Vec2& axis, const b2Color& color) = 0; /// Draw a line segment. virtual void DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color) = 0; /// Draw a transform. Choose your own length scale. /// @param xf a transform. virtual void DrawTransform(const b2Transform& xf) = 0; protected: uint32 m_drawFlags; }; #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Common/b2GrowableStack.h ================================================ /* * Copyright (c) 2010 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_GROWABLE_STACK_H #define B2_GROWABLE_STACK_H #include "b2Settings.h" #include #include /// This is a growable LIFO stack with an initial capacity of N. /// If the stack size exceeds the initial capacity, the heap is used /// to increase the size of the stack. template class b2GrowableStack { public: b2GrowableStack() { m_stack = m_array; m_count = 0; m_capacity = N; } ~b2GrowableStack() { if (m_stack != m_array) { b2Free(m_stack); m_stack = NULL; } } void Push(const T& element) { if (m_count == m_capacity) { T* old = m_stack; m_capacity *= 2; m_stack = (T*)b2Alloc(m_capacity * sizeof(T)); memcpy(m_stack, old, m_count * sizeof(T)); if (old != m_array) { b2Free(old); } } m_stack[m_count] = element; ++m_count; } T Pop() { b2Assert(m_count > 0); --m_count; return m_stack[m_count]; } int32 GetCount() { return m_count; } private: T* m_stack; T m_array[N]; int32 m_count; int32 m_capacity; }; #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Common/b2Math.h ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_MATH_H #define B2_MATH_H #include "b2Settings.h" #include /// This function is used to ensure that a floating point number is not a NaN or infinity. inline bool b2IsValid(float32 x) { int32 ix = *reinterpret_cast(&x); return (ix & 0x7f800000) != 0x7f800000; } /// This is a approximate yet fast inverse square-root. inline float32 b2InvSqrt(float32 x) { union { float32 x; int32 i; } convert; convert.x = x; float32 xhalf = 0.5f * x; convert.i = 0x5f3759df - (convert.i >> 1); x = convert.x; x = x * (1.5f - xhalf * x * x); return x; } #define b2Sqrt(x) sqrtf(x) #define b2Atan2(y, x) atan2f(y, x) /// A 2D column vector. struct b2Vec2 { /// Default constructor does nothing (for performance). b2Vec2() {} /// Construct using coordinates. b2Vec2(float32 x, float32 y) : x(x), y(y) {} /// Set this vector to all zeros. void SetZero() { x = 0.0f; y = 0.0f; } /// Set this vector to some specified coordinates. void Set(float32 x_, float32 y_) { x = x_; y = y_; } /// Negate this vector. b2Vec2 operator -() const { b2Vec2 v; v.Set(-x, -y); return v; } /// Read from and indexed element. float32 operator () (int32 i) const { return (&x)[i]; } /// Write to an indexed element. float32& operator () (int32 i) { return (&x)[i]; } /// Add a vector to this vector. void operator += (const b2Vec2& v) { x += v.x; y += v.y; } /// Subtract a vector from this vector. void operator -= (const b2Vec2& v) { x -= v.x; y -= v.y; } /// Multiply this vector by a scalar. void operator *= (float32 a) { x *= a; y *= a; } /// Get the length of this vector (the norm). float32 Length() const { return b2Sqrt(x * x + y * y); } /// Get the length squared. For performance, use this instead of /// b2Vec2::Length (if possible). float32 LengthSquared() const { return x * x + y * y; } /// Convert this vector into a unit vector. Returns the length. float32 Normalize() { float32 length = Length(); if (length < b2_epsilon) { return 0.0f; } float32 invLength = 1.0f / length; x *= invLength; y *= invLength; return length; } /// Does this vector contain finite coordinates? bool IsValid() const { return b2IsValid(x) && b2IsValid(y); } /// Get the skew vector such that dot(skew_vec, other) == cross(vec, other) b2Vec2 Skew() const { return b2Vec2(-y, x); } float32 x, y; }; /// A 2D column vector with 3 elements. struct b2Vec3 { /// Default constructor does nothing (for performance). b2Vec3() {} /// Construct using coordinates. b2Vec3(float32 x, float32 y, float32 z) : x(x), y(y), z(z) {} /// Set this vector to all zeros. void SetZero() { x = 0.0f; y = 0.0f; z = 0.0f; } /// Set this vector to some specified coordinates. void Set(float32 x_, float32 y_, float32 z_) { x = x_; y = y_; z = z_; } /// Negate this vector. b2Vec3 operator -() const { b2Vec3 v; v.Set(-x, -y, -z); return v; } /// Add a vector to this vector. void operator += (const b2Vec3& v) { x += v.x; y += v.y; z += v.z; } /// Subtract a vector from this vector. void operator -= (const b2Vec3& v) { x -= v.x; y -= v.y; z -= v.z; } /// Multiply this vector by a scalar. void operator *= (float32 s) { x *= s; y *= s; z *= s; } float32 x, y, z; }; /// A 2-by-2 matrix. Stored in column-major order. struct b2Mat22 { /// The default constructor does nothing (for performance). b2Mat22() {} /// Construct this matrix using columns. b2Mat22(const b2Vec2& c1, const b2Vec2& c2) { ex = c1; ey = c2; } /// Construct this matrix using scalars. b2Mat22(float32 a11, float32 a12, float32 a21, float32 a22) { ex.x = a11; ex.y = a21; ey.x = a12; ey.y = a22; } /// Initialize this matrix using columns. void Set(const b2Vec2& c1, const b2Vec2& c2) { ex = c1; ey = c2; } /// Set this to the identity matrix. void SetIdentity() { ex.x = 1.0f; ey.x = 0.0f; ex.y = 0.0f; ey.y = 1.0f; } /// Set this matrix to all zeros. void SetZero() { ex.x = 0.0f; ey.x = 0.0f; ex.y = 0.0f; ey.y = 0.0f; } b2Mat22 GetInverse() const { float32 a = ex.x, b = ey.x, c = ex.y, d = ey.y; b2Mat22 B; float32 det = a * d - b * c; if (det != 0.0f) { det = 1.0f / det; } B.ex.x = det * d; B.ey.x = -det * b; B.ex.y = -det * c; B.ey.y = det * a; return B; } /// Solve A * x = b, where b is a column vector. This is more efficient /// than computing the inverse in one-shot cases. b2Vec2 Solve(const b2Vec2& b) const { float32 a11 = ex.x, a12 = ey.x, a21 = ex.y, a22 = ey.y; float32 det = a11 * a22 - a12 * a21; if (det != 0.0f) { det = 1.0f / det; } b2Vec2 x; x.x = det * (a22 * b.x - a12 * b.y); x.y = det * (a11 * b.y - a21 * b.x); return x; } b2Vec2 ex, ey; }; /// A 3-by-3 matrix. Stored in column-major order. struct b2Mat33 { /// The default constructor does nothing (for performance). b2Mat33() {} /// Construct this matrix using columns. b2Mat33(const b2Vec3& c1, const b2Vec3& c2, const b2Vec3& c3) { ex = c1; ey = c2; ez = c3; } /// Set this matrix to all zeros. void SetZero() { ex.SetZero(); ey.SetZero(); ez.SetZero(); } /// Solve A * x = b, where b is a column vector. This is more efficient /// than computing the inverse in one-shot cases. b2Vec3 Solve33(const b2Vec3& b) const; /// Solve A * x = b, where b is a column vector. This is more efficient /// than computing the inverse in one-shot cases. Solve only the upper /// 2-by-2 matrix equation. b2Vec2 Solve22(const b2Vec2& b) const; /// Get the inverse of this matrix as a 2-by-2. /// Returns the zero matrix if singular. void GetInverse22(b2Mat33* M) const; /// Get the symmetric inverse of this matrix as a 3-by-3. /// Returns the zero matrix if singular. void GetSymInverse33(b2Mat33* M) const; b2Vec3 ex, ey, ez; }; /// Rotation struct b2Rot { b2Rot() {} /// Initialize from an angle in radians explicit b2Rot(float32 angle) { /// TODO_ERIN optimize s = sinf(angle); c = cosf(angle); } /// Set using an angle in radians. void Set(float32 angle) { /// TODO_ERIN optimize s = sinf(angle); c = cosf(angle); } /// Set to the identity rotation void SetIdentity() { s = 0.0f; c = 1.0f; } /// Get the angle in radians float32 GetAngle() const { return b2Atan2(s, c); } /// Get the x-axis b2Vec2 GetXAxis() const { return b2Vec2(c, s); } /// Get the u-axis b2Vec2 GetYAxis() const { return b2Vec2(-s, c); } /// Sine and cosine float32 s, c; }; /// A transform contains translation and rotation. It is used to represent /// the position and orientation of rigid frames. struct b2Transform { /// The default constructor does nothing. b2Transform() {} /// Initialize using a position vector and a rotation. b2Transform(const b2Vec2& position, const b2Rot& rotation) : p(position), q(rotation) {} /// Set this to the identity transform. void SetIdentity() { p.SetZero(); q.SetIdentity(); } /// Set this based on the position and angle. void Set(const b2Vec2& position, float32 angle) { p = position; q.Set(angle); } b2Vec2 p; b2Rot q; }; /// This describes the motion of a body/shape for TOI computation. /// Shapes are defined with respect to the body origin, which may /// no coincide with the center of mass. However, to support dynamics /// we must interpolate the center of mass position. struct b2Sweep { /// Get the interpolated transform at a specific time. /// @param beta is a factor in [0,1], where 0 indicates alpha0. void GetTransform(b2Transform* xfb, float32 beta) const; /// Advance the sweep forward, yielding a new initial state. /// @param alpha the new initial time. void Advance(float32 alpha); /// Normalize the angles. void Normalize(); b2Vec2 localCenter; ///< local center of mass position b2Vec2 c0, c; ///< center world positions float32 a0, a; ///< world angles /// Fraction of the current time step in the range [0,1] /// c0 and a0 are the positions at alpha0. float32 alpha0; }; /// Useful constant extern const b2Vec2 b2Vec2_zero; /// Perform the dot product on two vectors. inline float32 b2Dot(const b2Vec2& a, const b2Vec2& b) { return a.x * b.x + a.y * b.y; } /// Perform the cross product on two vectors. In 2D this produces a scalar. inline float32 b2Cross(const b2Vec2& a, const b2Vec2& b) { return a.x * b.y - a.y * b.x; } /// Perform the cross product on a vector and a scalar. In 2D this produces /// a vector. inline b2Vec2 b2Cross(const b2Vec2& a, float32 s) { return b2Vec2(s * a.y, -s * a.x); } /// Perform the cross product on a scalar and a vector. In 2D this produces /// a vector. inline b2Vec2 b2Cross(float32 s, const b2Vec2& a) { return b2Vec2(-s * a.y, s * a.x); } /// Multiply a matrix times a vector. If a rotation matrix is provided, /// then this transforms the vector from one frame to another. inline b2Vec2 b2Mul(const b2Mat22& A, const b2Vec2& v) { return b2Vec2(A.ex.x * v.x + A.ey.x * v.y, A.ex.y * v.x + A.ey.y * v.y); } /// Multiply a matrix transpose times a vector. If a rotation matrix is provided, /// then this transforms the vector from one frame to another (inverse transform). inline b2Vec2 b2MulT(const b2Mat22& A, const b2Vec2& v) { return b2Vec2(b2Dot(v, A.ex), b2Dot(v, A.ey)); } /// Add two vectors component-wise. inline b2Vec2 operator + (const b2Vec2& a, const b2Vec2& b) { return b2Vec2(a.x + b.x, a.y + b.y); } /// Subtract two vectors component-wise. inline b2Vec2 operator - (const b2Vec2& a, const b2Vec2& b) { return b2Vec2(a.x - b.x, a.y - b.y); } inline b2Vec2 operator * (float32 s, const b2Vec2& a) { return b2Vec2(s * a.x, s * a.y); } inline bool operator == (const b2Vec2& a, const b2Vec2& b) { return a.x == b.x && a.y == b.y; } inline float32 b2Distance(const b2Vec2& a, const b2Vec2& b) { b2Vec2 c = a - b; return c.Length(); } inline float32 b2DistanceSquared(const b2Vec2& a, const b2Vec2& b) { b2Vec2 c = a - b; return b2Dot(c, c); } inline b2Vec3 operator * (float32 s, const b2Vec3& a) { return b2Vec3(s * a.x, s * a.y, s * a.z); } /// Add two vectors component-wise. inline b2Vec3 operator + (const b2Vec3& a, const b2Vec3& b) { return b2Vec3(a.x + b.x, a.y + b.y, a.z + b.z); } /// Subtract two vectors component-wise. inline b2Vec3 operator - (const b2Vec3& a, const b2Vec3& b) { return b2Vec3(a.x - b.x, a.y - b.y, a.z - b.z); } /// Perform the dot product on two vectors. inline float32 b2Dot(const b2Vec3& a, const b2Vec3& b) { return a.x * b.x + a.y * b.y + a.z * b.z; } /// Perform the cross product on two vectors. inline b2Vec3 b2Cross(const b2Vec3& a, const b2Vec3& b) { return b2Vec3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); } inline b2Mat22 operator + (const b2Mat22& A, const b2Mat22& B) { return b2Mat22(A.ex + B.ex, A.ey + B.ey); } // A * B inline b2Mat22 b2Mul(const b2Mat22& A, const b2Mat22& B) { return b2Mat22(b2Mul(A, B.ex), b2Mul(A, B.ey)); } // A^T * B inline b2Mat22 b2MulT(const b2Mat22& A, const b2Mat22& B) { b2Vec2 c1(b2Dot(A.ex, B.ex), b2Dot(A.ey, B.ex)); b2Vec2 c2(b2Dot(A.ex, B.ey), b2Dot(A.ey, B.ey)); return b2Mat22(c1, c2); } /// Multiply a matrix times a vector. inline b2Vec3 b2Mul(const b2Mat33& A, const b2Vec3& v) { return v.x * A.ex + v.y * A.ey + v.z * A.ez; } /// Multiply a matrix times a vector. inline b2Vec2 b2Mul22(const b2Mat33& A, const b2Vec2& v) { return b2Vec2(A.ex.x * v.x + A.ey.x * v.y, A.ex.y * v.x + A.ey.y * v.y); } /// Multiply two rotations: q * r inline b2Rot b2Mul(const b2Rot& q, const b2Rot& r) { // [qc -qs] * [rc -rs] = [qc*rc-qs*rs -qc*rs-qs*rc] // [qs qc] [rs rc] [qs*rc+qc*rs -qs*rs+qc*rc] // s = qs * rc + qc * rs // c = qc * rc - qs * rs b2Rot qr; qr.s = q.s * r.c + q.c * r.s; qr.c = q.c * r.c - q.s * r.s; return qr; } /// Transpose multiply two rotations: qT * r inline b2Rot b2MulT(const b2Rot& q, const b2Rot& r) { // [ qc qs] * [rc -rs] = [qc*rc+qs*rs -qc*rs+qs*rc] // [-qs qc] [rs rc] [-qs*rc+qc*rs qs*rs+qc*rc] // s = qc * rs - qs * rc // c = qc * rc + qs * rs b2Rot qr; qr.s = q.c * r.s - q.s * r.c; qr.c = q.c * r.c + q.s * r.s; return qr; } /// Rotate a vector inline b2Vec2 b2Mul(const b2Rot& q, const b2Vec2& v) { return b2Vec2(q.c * v.x - q.s * v.y, q.s * v.x + q.c * v.y); } /// Inverse rotate a vector inline b2Vec2 b2MulT(const b2Rot& q, const b2Vec2& v) { return b2Vec2(q.c * v.x + q.s * v.y, -q.s * v.x + q.c * v.y); } inline b2Vec2 b2Mul(const b2Transform& T, const b2Vec2& v) { float32 x = (T.q.c * v.x - T.q.s * v.y) + T.p.x; float32 y = (T.q.s * v.x + T.q.c * v.y) + T.p.y; return b2Vec2(x, y); } inline b2Vec2 b2MulT(const b2Transform& T, const b2Vec2& v) { float32 px = v.x - T.p.x; float32 py = v.y - T.p.y; float32 x = (T.q.c * px + T.q.s * py); float32 y = (-T.q.s * px + T.q.c * py); return b2Vec2(x, y); } // v2 = A.q.Rot(B.q.Rot(v1) + B.p) + A.p // = (A.q * B.q).Rot(v1) + A.q.Rot(B.p) + A.p inline b2Transform b2Mul(const b2Transform& A, const b2Transform& B) { b2Transform C; C.q = b2Mul(A.q, B.q); C.p = b2Mul(A.q, B.p) + A.p; return C; } // v2 = A.q' * (B.q * v1 + B.p - A.p) // = A.q' * B.q * v1 + A.q' * (B.p - A.p) inline b2Transform b2MulT(const b2Transform& A, const b2Transform& B) { b2Transform C; C.q = b2MulT(A.q, B.q); C.p = b2MulT(A.q, B.p - A.p); return C; } template inline T b2Abs(T a) { return a > T(0) ? a : -a; } inline b2Vec2 b2Abs(const b2Vec2& a) { return b2Vec2(b2Abs(a.x), b2Abs(a.y)); } inline b2Mat22 b2Abs(const b2Mat22& A) { return b2Mat22(b2Abs(A.ex), b2Abs(A.ey)); } template inline T b2Min(T a, T b) { return a < b ? a : b; } inline b2Vec2 b2Min(const b2Vec2& a, const b2Vec2& b) { return b2Vec2(b2Min(a.x, b.x), b2Min(a.y, b.y)); } template inline T b2Max(T a, T b) { return a > b ? a : b; } inline b2Vec2 b2Max(const b2Vec2& a, const b2Vec2& b) { return b2Vec2(b2Max(a.x, b.x), b2Max(a.y, b.y)); } template inline T b2Clamp(T a, T low, T high) { return b2Max(low, b2Min(a, high)); } inline b2Vec2 b2Clamp(const b2Vec2& a, const b2Vec2& low, const b2Vec2& high) { return b2Max(low, b2Min(a, high)); } template inline void b2Swap(T& a, T& b) { T tmp = a; a = b; b = tmp; } /// "Next Largest Power of 2 /// Given a binary integer value x, the next largest power of 2 can be computed by a SWAR algorithm /// that recursively "folds" the upper bits into the lower bits. This process yields a bit vector with /// the same most significant 1 as x, but all 1's below it. Adding 1 to that value yields the next /// largest power of 2. For a 32-bit value:" inline uint32 b2NextPowerOfTwo(uint32 x) { x |= (x >> 1); x |= (x >> 2); x |= (x >> 4); x |= (x >> 8); x |= (x >> 16); return x + 1; } inline bool b2IsPowerOfTwo(uint32 x) { bool result = x > 0 && (x & (x - 1)) == 0; return result; } inline void b2Sweep::GetTransform(b2Transform* xf, float32 beta) const { xf->p = (1.0f - beta) * c0 + beta * c; float32 angle = (1.0f - beta) * a0 + beta * a; xf->q.Set(angle); // Shift to origin xf->p -= b2Mul(xf->q, localCenter); } inline void b2Sweep::Advance(float32 alpha) { b2Assert(alpha0 < 1.0f); float32 beta = (alpha - alpha0) / (1.0f - alpha0); c0 += beta * (c - c0); a0 += beta * (a - a0); alpha0 = alpha; } /// Normalize an angle in radians to be between -pi and pi inline void b2Sweep::Normalize() { float32 twoPi = 2.0f * b2_pi; float32 d = twoPi * floorf(a0 / twoPi); a0 -= d; a -= d; } #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Common/b2Settings.h ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_SETTINGS_H #define B2_SETTINGS_H #include #include #include #define B2_NOT_USED(x) ((void)(x)) #define b2Assert(A) assert(A) typedef signed char int8; typedef signed short int16; typedef signed int int32; typedef unsigned char uint8; typedef unsigned short uint16; typedef unsigned int uint32; typedef float float32; typedef double float64; #define b2_maxFloat FLT_MAX #define b2_epsilon FLT_EPSILON #define b2_pi 3.14159265359f /// @file /// Global tuning constants based on meters-kilograms-seconds (MKS) units. /// // Collision /// The maximum number of contact points between two convex shapes. Do /// not change this value. #define b2_maxManifoldPoints 2 /// The maximum number of vertices on a convex polygon. You cannot increase /// this too much because b2BlockAllocator has a maximum object size. #define b2_maxPolygonVertices 8 /// This is used to fatten AABBs in the dynamic tree. This allows proxies /// to move by a small amount without triggering a tree adjustment. /// This is in meters. #define b2_aabbExtension 0.1f /// This is used to fatten AABBs in the dynamic tree. This is used to predict /// the future position based on the current displacement. /// This is a dimensionless multiplier. #define b2_aabbMultiplier 2.0f /// A small length used as a collision and constraint tolerance. Usually it is /// chosen to be numerically significant, but visually insignificant. #define b2_linearSlop 0.005f /// A small angle used as a collision and constraint tolerance. Usually it is /// chosen to be numerically significant, but visually insignificant. #define b2_angularSlop (2.0f / 180.0f * b2_pi) /// The radius of the polygon/edge shape skin. This should not be modified. Making /// this smaller means polygons will have an insufficient buffer for continuous collision. /// Making it larger may create artifacts for vertex collision. #define b2_polygonRadius (2.0f * b2_linearSlop) /// Maximum number of sub-steps per contact in continuous physics simulation. #define b2_maxSubSteps 8 // Dynamics /// Maximum number of contacts to be handled to solve a TOI impact. #define b2_maxTOIContacts 32 /// A velocity threshold for elastic collisions. Any collision with a relative linear /// velocity below this threshold will be treated as inelastic. #define b2_velocityThreshold 1.0f /// The maximum linear position correction used when solving constraints. This helps to /// prevent overshoot. #define b2_maxLinearCorrection 0.2f /// The maximum angular position correction used when solving constraints. This helps to /// prevent overshoot. #define b2_maxAngularCorrection (8.0f / 180.0f * b2_pi) /// The maximum linear velocity of a body. This limit is very large and is used /// to prevent numerical problems. You shouldn't need to adjust this. #define b2_maxTranslation 2.0f #define b2_maxTranslationSquared (b2_maxTranslation * b2_maxTranslation) /// The maximum angular velocity of a body. This limit is very large and is used /// to prevent numerical problems. You shouldn't need to adjust this. #define b2_maxRotation (0.5f * b2_pi) #define b2_maxRotationSquared (b2_maxRotation * b2_maxRotation) /// This scale factor controls how fast overlap is resolved. Ideally this would be 1 so /// that overlap is removed in one time step. However using values close to 1 often lead /// to overshoot. #define b2_baumgarte 0.2f #define b2_toiBaugarte 0.75f // Sleep /// The time that a body must be still before it will go to sleep. #define b2_timeToSleep 0.5f /// A body cannot sleep if its linear velocity is above this tolerance. #define b2_linearSleepTolerance 0.01f /// A body cannot sleep if its angular velocity is above this tolerance. #define b2_angularSleepTolerance (2.0f / 180.0f * b2_pi) // Memory Allocation /// Implement this function to use your own memory allocator. void* b2Alloc(int32 size); /// If you implement b2Alloc, you should also implement this function. void b2Free(void* mem); /// Logging function. void b2Log(const char* string, ...); /// Version numbering scheme. /// See http://en.wikipedia.org/wiki/Software_versioning struct b2Version { int32 major; ///< significant changes int32 minor; ///< incremental changes int32 revision; ///< bug fixes }; /// Current version. extern b2Version b2_version; #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Common/b2StackAllocator.h ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_STACK_ALLOCATOR_H #define B2_STACK_ALLOCATOR_H #include "b2Settings.h" const int32 b2_stackSize = 100 * 1024; // 100k const int32 b2_maxStackEntries = 32; struct b2StackEntry { char* data; int32 size; bool usedMalloc; }; // This is a stack allocator used for fast per step allocations. // You must nest allocate/free pairs. The code will assert // if you try to interleave multiple allocate/free pairs. class b2StackAllocator { public: b2StackAllocator(); ~b2StackAllocator(); void* Allocate(int32 size); void Free(void* p); int32 GetMaxAllocation() const; private: char m_data[b2_stackSize]; int32 m_index; int32 m_allocation; int32 m_maxAllocation; b2StackEntry m_entries[b2_maxStackEntries]; int32 m_entryCount; }; #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Common/b2Timer.h ================================================ /* * Copyright (c) 2011 Erin Catto http://box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_TIMER_H #define B2_TIMER_H #include "b2Settings.h" /// Timer for profiling. This has platform specific code and may /// not work on every platform. class b2Timer { public: /// Constructor b2Timer(); /// Reset the timer. void Reset(); /// Get the time since construction or the last reset. float32 GetMilliseconds() const; private: #if defined(_WIN32) float64 m_start; static float64 s_invFrequency; #elif defined(__linux__) || defined (__APPLE__) unsigned long m_start_sec; unsigned long m_start_usec; #endif }; #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/Contacts/b2ChainAndCircleContact.h ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_CHAIN_AND_CIRCLE_CONTACT_H #define B2_CHAIN_AND_CIRCLE_CONTACT_H #include "b2Contact.h" class b2BlockAllocator; class b2ChainAndCircleContact : public b2Contact { public: static b2Contact* Create( b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB, b2BlockAllocator* allocator); static void Destroy(b2Contact* contact, b2BlockAllocator* allocator); b2ChainAndCircleContact(b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB); ~b2ChainAndCircleContact() {} void Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB); }; #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/Contacts/b2ChainAndPolygonContact.h ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_CHAIN_AND_POLYGON_CONTACT_H #define B2_CHAIN_AND_POLYGON_CONTACT_H #include "b2Contact.h" class b2BlockAllocator; class b2ChainAndPolygonContact : public b2Contact { public: static b2Contact* Create( b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB, b2BlockAllocator* allocator); static void Destroy(b2Contact* contact, b2BlockAllocator* allocator); b2ChainAndPolygonContact(b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB); ~b2ChainAndPolygonContact() {} void Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB); }; #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/Contacts/b2CircleContact.h ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_CIRCLE_CONTACT_H #define B2_CIRCLE_CONTACT_H #include "b2Contact.h" class b2BlockAllocator; class b2CircleContact : public b2Contact { public: static b2Contact* Create( b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB, b2BlockAllocator* allocator); static void Destroy(b2Contact* contact, b2BlockAllocator* allocator); b2CircleContact(b2Fixture* fixtureA, b2Fixture* fixtureB); ~b2CircleContact() {} void Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB); }; #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/Contacts/b2Contact.h ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_CONTACT_H #define B2_CONTACT_H #include "../../Common/b2Math.h" #include "../../Collision/b2Collision.h" #include "../../Collision/Shapes/b2Shape.h" #include "../b2Fixture.h" class b2Body; class b2Contact; class b2Fixture; class b2World; class b2BlockAllocator; class b2StackAllocator; class b2ContactListener; /// Friction mixing law. The idea is to allow either fixture to drive the restitution to zero. /// For example, anything slides on ice. inline float32 b2MixFriction(float32 friction1, float32 friction2) { return b2Sqrt(friction1 * friction2); } /// Restitution mixing law. The idea is allow for anything to bounce off an inelastic surface. /// For example, a superball bounces on anything. inline float32 b2MixRestitution(float32 restitution1, float32 restitution2) { return restitution1 > restitution2 ? restitution1 : restitution2; } typedef b2Contact* b2ContactCreateFcn( b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB, b2BlockAllocator* allocator); typedef void b2ContactDestroyFcn(b2Contact* contact, b2BlockAllocator* allocator); struct b2ContactRegister { b2ContactCreateFcn* createFcn; b2ContactDestroyFcn* destroyFcn; bool primary; }; /// A contact edge is used to connect bodies and contacts together /// in a contact graph where each body is a node and each contact /// is an edge. A contact edge belongs to a doubly linked list /// maintained in each attached body. Each contact has two contact /// nodes, one for each attached body. struct b2ContactEdge { b2Body* other; ///< provides quick access to the other body attached. b2Contact* contact; ///< the contact b2ContactEdge* prev; ///< the previous contact edge in the body's contact list b2ContactEdge* next; ///< the next contact edge in the body's contact list }; /// The class manages contact between two shapes. A contact exists for each overlapping /// AABB in the broad-phase (except if filtered). Therefore a contact object may exist /// that has no contact points. class b2Contact { public: /// Get the contact manifold. Do not modify the manifold unless you understand the /// internals of Box2D. b2Manifold* GetManifold(); const b2Manifold* GetManifold() const; /// Get the world manifold. void GetWorldManifold(b2WorldManifold* worldManifold) const; /// Is this contact touching? bool IsTouching() const; /// Enable/disable this contact. This can be used inside the pre-solve /// contact listener. The contact is only disabled for the current /// time step (or sub-step in continuous collisions). void SetEnabled(bool flag); /// Has this contact been disabled? bool IsEnabled() const; /// Get the next contact in the world's contact list. b2Contact* GetNext(); const b2Contact* GetNext() const; /// Get fixture A in this contact. b2Fixture* GetFixtureA(); const b2Fixture* GetFixtureA() const; /// Get the child primitive index for fixture A. int32 GetChildIndexA() const; /// Get fixture B in this contact. b2Fixture* GetFixtureB(); const b2Fixture* GetFixtureB() const; /// Get the child primitive index for fixture B. int32 GetChildIndexB() const; /// Override the default friction mixture. You can call this in b2ContactListener::PreSolve. /// This value persists until set or reset. void SetFriction(float32 friction); /// Get the friction. float32 GetFriction() const; /// Reset the friction mixture to the default value. void ResetFriction(); /// Override the default restitution mixture. You can call this in b2ContactListener::PreSolve. /// The value persists until you set or reset. void SetRestitution(float32 restitution); /// Get the restitution. float32 GetRestitution() const; /// Reset the restitution to the default value. void ResetRestitution(); /// Set the desired tangent speed for a conveyor belt behavior. In meters per second. void SetTangentSpeed(float32 speed); /// Get the desired tangent speed. In meters per second. float32 GetTangentSpeed() const; /// Evaluate this contact with your own manifold and transforms. virtual void Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB) = 0; protected: friend class b2ContactManager; friend class b2World; friend class b2ContactSolver; friend class b2Body; friend class b2Fixture; // Flags stored in m_flags enum { // Used when crawling contact graph when forming islands. e_islandFlag = 0x0001, // Set when the shapes are touching. e_touchingFlag = 0x0002, // This contact can be disabled (by user) e_enabledFlag = 0x0004, // This contact needs filtering because a fixture filter was changed. e_filterFlag = 0x0008, // This bullet contact had a TOI event e_bulletHitFlag = 0x0010, // This contact has a valid TOI in m_toi e_toiFlag = 0x0020 }; /// Flag this contact for filtering. Filtering will occur the next time step. void FlagForFiltering(); static void AddType(b2ContactCreateFcn* createFcn, b2ContactDestroyFcn* destroyFcn, b2Shape::Type typeA, b2Shape::Type typeB); static void InitializeRegisters(); static b2Contact* Create(b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB, b2BlockAllocator* allocator); static void Destroy(b2Contact* contact, b2Shape::Type typeA, b2Shape::Type typeB, b2BlockAllocator* allocator); static void Destroy(b2Contact* contact, b2BlockAllocator* allocator); b2Contact() : m_fixtureA(NULL), m_fixtureB(NULL) {} b2Contact(b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB); virtual ~b2Contact() {} void Update(b2ContactListener* listener); static b2ContactRegister s_registers[b2Shape::e_typeCount][b2Shape::e_typeCount]; static bool s_initialized; uint32 m_flags; // World pool and list pointers. b2Contact* m_prev; b2Contact* m_next; // Nodes for connecting bodies. b2ContactEdge m_nodeA; b2ContactEdge m_nodeB; b2Fixture* m_fixtureA; b2Fixture* m_fixtureB; int32 m_indexA; int32 m_indexB; b2Manifold m_manifold; int32 m_toiCount; float32 m_toi; float32 m_friction; float32 m_restitution; float32 m_tangentSpeed; }; inline b2Manifold* b2Contact::GetManifold() { return &m_manifold; } inline const b2Manifold* b2Contact::GetManifold() const { return &m_manifold; } inline void b2Contact::GetWorldManifold(b2WorldManifold* worldManifold) const { const b2Body* bodyA = m_fixtureA->GetBody(); const b2Body* bodyB = m_fixtureB->GetBody(); const b2Shape* shapeA = m_fixtureA->GetShape(); const b2Shape* shapeB = m_fixtureB->GetShape(); worldManifold->Initialize(&m_manifold, bodyA->GetTransform(), shapeA->m_radius, bodyB->GetTransform(), shapeB->m_radius); } inline void b2Contact::SetEnabled(bool flag) { if (flag) { m_flags |= e_enabledFlag; } else { m_flags &= ~e_enabledFlag; } } inline bool b2Contact::IsEnabled() const { return (m_flags & e_enabledFlag) == e_enabledFlag; } inline bool b2Contact::IsTouching() const { return (m_flags & e_touchingFlag) == e_touchingFlag; } inline b2Contact* b2Contact::GetNext() { return m_next; } inline const b2Contact* b2Contact::GetNext() const { return m_next; } inline b2Fixture* b2Contact::GetFixtureA() { return m_fixtureA; } inline const b2Fixture* b2Contact::GetFixtureA() const { return m_fixtureA; } inline b2Fixture* b2Contact::GetFixtureB() { return m_fixtureB; } inline int32 b2Contact::GetChildIndexA() const { return m_indexA; } inline const b2Fixture* b2Contact::GetFixtureB() const { return m_fixtureB; } inline int32 b2Contact::GetChildIndexB() const { return m_indexB; } inline void b2Contact::FlagForFiltering() { m_flags |= e_filterFlag; } inline void b2Contact::SetFriction(float32 friction) { m_friction = friction; } inline float32 b2Contact::GetFriction() const { return m_friction; } inline void b2Contact::ResetFriction() { m_friction = b2MixFriction(m_fixtureA->m_friction, m_fixtureB->m_friction); } inline void b2Contact::SetRestitution(float32 restitution) { m_restitution = restitution; } inline float32 b2Contact::GetRestitution() const { return m_restitution; } inline void b2Contact::ResetRestitution() { m_restitution = b2MixRestitution(m_fixtureA->m_restitution, m_fixtureB->m_restitution); } inline void b2Contact::SetTangentSpeed(float32 speed) { m_tangentSpeed = speed; } inline float32 b2Contact::GetTangentSpeed() const { return m_tangentSpeed; } #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/Contacts/b2ContactSolver.h ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_CONTACT_SOLVER_H #define B2_CONTACT_SOLVER_H #include "../../Common/b2Math.h" #include "../../Collision/b2Collision.h" #include "../b2TimeStep.h" class b2Contact; class b2Body; class b2StackAllocator; struct b2ContactPositionConstraint; struct b2VelocityConstraintPoint { b2Vec2 rA; b2Vec2 rB; float32 normalImpulse; float32 tangentImpulse; float32 normalMass; float32 tangentMass; float32 velocityBias; }; struct b2ContactVelocityConstraint { b2VelocityConstraintPoint points[b2_maxManifoldPoints]; b2Vec2 normal; b2Mat22 normalMass; b2Mat22 K; int32 indexA; int32 indexB; float32 invMassA, invMassB; float32 invIA, invIB; float32 friction; float32 restitution; float32 tangentSpeed; int32 pointCount; int32 contactIndex; }; struct b2ContactSolverDef { b2TimeStep step; b2Contact** contacts; int32 count; b2Position* positions; b2Velocity* velocities; b2StackAllocator* allocator; }; class b2ContactSolver { public: b2ContactSolver(b2ContactSolverDef* def); ~b2ContactSolver(); void InitializeVelocityConstraints(); void WarmStart(); void SolveVelocityConstraints(); void StoreImpulses(); bool SolvePositionConstraints(); bool SolveTOIPositionConstraints(int32 toiIndexA, int32 toiIndexB); b2TimeStep m_step; b2Position* m_positions; b2Velocity* m_velocities; b2StackAllocator* m_allocator; b2ContactPositionConstraint* m_positionConstraints; b2ContactVelocityConstraint* m_velocityConstraints; b2Contact** m_contacts; int m_count; }; #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/Contacts/b2EdgeAndCircleContact.h ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_EDGE_AND_CIRCLE_CONTACT_H #define B2_EDGE_AND_CIRCLE_CONTACT_H #include "b2Contact.h" class b2BlockAllocator; class b2EdgeAndCircleContact : public b2Contact { public: static b2Contact* Create( b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB, b2BlockAllocator* allocator); static void Destroy(b2Contact* contact, b2BlockAllocator* allocator); b2EdgeAndCircleContact(b2Fixture* fixtureA, b2Fixture* fixtureB); ~b2EdgeAndCircleContact() {} void Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB); }; #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/Contacts/b2EdgeAndPolygonContact.h ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_EDGE_AND_POLYGON_CONTACT_H #define B2_EDGE_AND_POLYGON_CONTACT_H #include "b2Contact.h" class b2BlockAllocator; class b2EdgeAndPolygonContact : public b2Contact { public: static b2Contact* Create( b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB, b2BlockAllocator* allocator); static void Destroy(b2Contact* contact, b2BlockAllocator* allocator); b2EdgeAndPolygonContact(b2Fixture* fixtureA, b2Fixture* fixtureB); ~b2EdgeAndPolygonContact() {} void Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB); }; #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/Contacts/b2PolygonAndCircleContact.h ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_POLYGON_AND_CIRCLE_CONTACT_H #define B2_POLYGON_AND_CIRCLE_CONTACT_H #include "b2Contact.h" class b2BlockAllocator; class b2PolygonAndCircleContact : public b2Contact { public: static b2Contact* Create(b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB, b2BlockAllocator* allocator); static void Destroy(b2Contact* contact, b2BlockAllocator* allocator); b2PolygonAndCircleContact(b2Fixture* fixtureA, b2Fixture* fixtureB); ~b2PolygonAndCircleContact() {} void Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB); }; #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/Contacts/b2PolygonContact.h ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_POLYGON_CONTACT_H #define B2_POLYGON_CONTACT_H #include "b2Contact.h" class b2BlockAllocator; class b2PolygonContact : public b2Contact { public: static b2Contact* Create( b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB, b2BlockAllocator* allocator); static void Destroy(b2Contact* contact, b2BlockAllocator* allocator); b2PolygonContact(b2Fixture* fixtureA, b2Fixture* fixtureB); ~b2PolygonContact() {} void Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB); }; #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/Joints/b2DistanceJoint.h ================================================ /* * Copyright (c) 2006-2007 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_DISTANCE_JOINT_H #define B2_DISTANCE_JOINT_H #include "b2Joint.h" /// Distance joint definition. This requires defining an /// anchor point on both bodies and the non-zero length of the /// distance joint. The definition uses local anchor points /// so that the initial configuration can violate the constraint /// slightly. This helps when saving and loading a game. /// @warning Do not use a zero or short length. struct b2DistanceJointDef : public b2JointDef { b2DistanceJointDef() { type = e_distanceJoint; localAnchorA.Set(0.0f, 0.0f); localAnchorB.Set(0.0f, 0.0f); length = 1.0f; frequencyHz = 0.0f; dampingRatio = 0.0f; } /// Initialize the bodies, anchors, and length using the world /// anchors. void Initialize(b2Body* bodyA, b2Body* bodyB, const b2Vec2& anchorA, const b2Vec2& anchorB); /// The local anchor point relative to bodyA's origin. b2Vec2 localAnchorA; /// The local anchor point relative to bodyB's origin. b2Vec2 localAnchorB; /// The natural length between the anchor points. float32 length; /// The mass-spring-damper frequency in Hertz. A value of 0 /// disables softness. float32 frequencyHz; /// The damping ratio. 0 = no damping, 1 = critical damping. float32 dampingRatio; }; /// A distance joint constrains two points on two bodies /// to remain at a fixed distance from each other. You can view /// this as a massless, rigid rod. class b2DistanceJoint : public b2Joint { public: b2Vec2 GetAnchorA() const; b2Vec2 GetAnchorB() const; /// Get the reaction force given the inverse time step. /// Unit is N. b2Vec2 GetReactionForce(float32 inv_dt) const; /// Get the reaction torque given the inverse time step. /// Unit is N*m. This is always zero for a distance joint. float32 GetReactionTorque(float32 inv_dt) const; /// The local anchor point relative to bodyA's origin. const b2Vec2& GetLocalAnchorA() const { return m_localAnchorA; } /// The local anchor point relative to bodyB's origin. const b2Vec2& GetLocalAnchorB() const { return m_localAnchorB; } /// Set/get the natural length. /// Manipulating the length can lead to non-physical behavior when the frequency is zero. void SetLength(float32 length); float32 GetLength() const; /// Set/get frequency in Hz. void SetFrequency(float32 hz); float32 GetFrequency() const; /// Set/get damping ratio. void SetDampingRatio(float32 ratio); float32 GetDampingRatio() const; /// Dump joint to dmLog void Dump(); protected: friend class b2Joint; b2DistanceJoint(const b2DistanceJointDef* data); void InitVelocityConstraints(const b2SolverData& data); void SolveVelocityConstraints(const b2SolverData& data); bool SolvePositionConstraints(const b2SolverData& data); float32 m_frequencyHz; float32 m_dampingRatio; float32 m_bias; // Solver shared b2Vec2 m_localAnchorA; b2Vec2 m_localAnchorB; float32 m_gamma; float32 m_impulse; float32 m_length; // Solver temp int32 m_indexA; int32 m_indexB; b2Vec2 m_u; b2Vec2 m_rA; b2Vec2 m_rB; b2Vec2 m_localCenterA; b2Vec2 m_localCenterB; float32 m_invMassA; float32 m_invMassB; float32 m_invIA; float32 m_invIB; float32 m_mass; }; inline void b2DistanceJoint::SetLength(float32 length) { m_length = length; } inline float32 b2DistanceJoint::GetLength() const { return m_length; } inline void b2DistanceJoint::SetFrequency(float32 hz) { m_frequencyHz = hz; } inline float32 b2DistanceJoint::GetFrequency() const { return m_frequencyHz; } inline void b2DistanceJoint::SetDampingRatio(float32 ratio) { m_dampingRatio = ratio; } inline float32 b2DistanceJoint::GetDampingRatio() const { return m_dampingRatio; } #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/Joints/b2FrictionJoint.h ================================================ /* * Copyright (c) 2006-2007 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_FRICTION_JOINT_H #define B2_FRICTION_JOINT_H #include "b2Joint.h" /// Friction joint definition. struct b2FrictionJointDef : public b2JointDef { b2FrictionJointDef() { type = e_frictionJoint; localAnchorA.SetZero(); localAnchorB.SetZero(); maxForce = 0.0f; maxTorque = 0.0f; } /// Initialize the bodies, anchors, axis, and reference angle using the world /// anchor and world axis. void Initialize(b2Body* bodyA, b2Body* bodyB, const b2Vec2& anchor); /// The local anchor point relative to bodyA's origin. b2Vec2 localAnchorA; /// The local anchor point relative to bodyB's origin. b2Vec2 localAnchorB; /// The maximum friction force in N. float32 maxForce; /// The maximum friction torque in N-m. float32 maxTorque; }; /// Friction joint. This is used for top-down friction. /// It provides 2D translational friction and angular friction. class b2FrictionJoint : public b2Joint { public: b2Vec2 GetAnchorA() const; b2Vec2 GetAnchorB() const; b2Vec2 GetReactionForce(float32 inv_dt) const; float32 GetReactionTorque(float32 inv_dt) const; /// The local anchor point relative to bodyA's origin. const b2Vec2& GetLocalAnchorA() const { return m_localAnchorA; } /// The local anchor point relative to bodyB's origin. const b2Vec2& GetLocalAnchorB() const { return m_localAnchorB; } /// Set the maximum friction force in N. void SetMaxForce(float32 force); /// Get the maximum friction force in N. float32 GetMaxForce() const; /// Set the maximum friction torque in N*m. void SetMaxTorque(float32 torque); /// Get the maximum friction torque in N*m. float32 GetMaxTorque() const; /// Dump joint to dmLog void Dump(); protected: friend class b2Joint; b2FrictionJoint(const b2FrictionJointDef* def); void InitVelocityConstraints(const b2SolverData& data); void SolveVelocityConstraints(const b2SolverData& data); bool SolvePositionConstraints(const b2SolverData& data); b2Vec2 m_localAnchorA; b2Vec2 m_localAnchorB; // Solver shared b2Vec2 m_linearImpulse; float32 m_angularImpulse; float32 m_maxForce; float32 m_maxTorque; // Solver temp int32 m_indexA; int32 m_indexB; b2Vec2 m_rA; b2Vec2 m_rB; b2Vec2 m_localCenterA; b2Vec2 m_localCenterB; float32 m_invMassA; float32 m_invMassB; float32 m_invIA; float32 m_invIB; b2Mat22 m_linearMass; float32 m_angularMass; }; #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/Joints/b2GearJoint.h ================================================ /* * Copyright (c) 2006-2011 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_GEAR_JOINT_H #define B2_GEAR_JOINT_H #include "b2Joint.h" /// Gear joint definition. This definition requires two existing /// revolute or prismatic joints (any combination will work). struct b2GearJointDef : public b2JointDef { b2GearJointDef() { type = e_gearJoint; joint1 = NULL; joint2 = NULL; ratio = 1.0f; } /// The first revolute/prismatic joint attached to the gear joint. b2Joint* joint1; /// The second revolute/prismatic joint attached to the gear joint. b2Joint* joint2; /// The gear ratio. /// @see b2GearJoint for explanation. float32 ratio; }; /// A gear joint is used to connect two joints together. Either joint /// can be a revolute or prismatic joint. You specify a gear ratio /// to bind the motions together: /// coordinate1 + ratio * coordinate2 = constant /// The ratio can be negative or positive. If one joint is a revolute joint /// and the other joint is a prismatic joint, then the ratio will have units /// of length or units of 1/length. /// @warning You have to manually destroy the gear joint if joint1 or joint2 /// is destroyed. class b2GearJoint : public b2Joint { public: b2Vec2 GetAnchorA() const; b2Vec2 GetAnchorB() const; b2Vec2 GetReactionForce(float32 inv_dt) const; float32 GetReactionTorque(float32 inv_dt) const; /// Get the first joint. b2Joint* GetJoint1() { return m_joint1; } /// Get the second joint. b2Joint* GetJoint2() { return m_joint2; } /// Set/Get the gear ratio. void SetRatio(float32 ratio); float32 GetRatio() const; /// Dump joint to dmLog void Dump(); protected: friend class b2Joint; b2GearJoint(const b2GearJointDef* data); void InitVelocityConstraints(const b2SolverData& data); void SolveVelocityConstraints(const b2SolverData& data); bool SolvePositionConstraints(const b2SolverData& data); b2Joint* m_joint1; b2Joint* m_joint2; b2JointType m_typeA; b2JointType m_typeB; // Body A is connected to body C // Body B is connected to body D b2Body* m_bodyC; b2Body* m_bodyD; // Solver shared b2Vec2 m_localAnchorA; b2Vec2 m_localAnchorB; b2Vec2 m_localAnchorC; b2Vec2 m_localAnchorD; b2Vec2 m_localAxisC; b2Vec2 m_localAxisD; float32 m_referenceAngleA; float32 m_referenceAngleB; float32 m_constant; float32 m_ratio; float32 m_impulse; // Solver temp int32 m_indexA, m_indexB, m_indexC, m_indexD; b2Vec2 m_lcA, m_lcB, m_lcC, m_lcD; float32 m_mA, m_mB, m_mC, m_mD; float32 m_iA, m_iB, m_iC, m_iD; b2Vec2 m_JvAC, m_JvBD; float32 m_JwA, m_JwB, m_JwC, m_JwD; float32 m_mass; }; #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/Joints/b2Joint.h ================================================ /* * Copyright (c) 2006-2007 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_JOINT_H #define B2_JOINT_H #include "../../Common/b2Math.h" class b2Body; class b2Joint; struct b2SolverData; class b2BlockAllocator; enum b2JointType { e_unknownJoint, e_revoluteJoint, e_prismaticJoint, e_distanceJoint, e_pulleyJoint, e_mouseJoint, e_gearJoint, e_wheelJoint, e_weldJoint, e_frictionJoint, e_ropeJoint, e_motorJoint }; enum b2LimitState { e_inactiveLimit, e_atLowerLimit, e_atUpperLimit, e_equalLimits }; struct b2Jacobian { b2Vec2 linear; float32 angularA; float32 angularB; }; /// A joint edge is used to connect bodies and joints together /// in a joint graph where each body is a node and each joint /// is an edge. A joint edge belongs to a doubly linked list /// maintained in each attached body. Each joint has two joint /// nodes, one for each attached body. struct b2JointEdge { b2Body* other; ///< provides quick access to the other body attached. b2Joint* joint; ///< the joint b2JointEdge* prev; ///< the previous joint edge in the body's joint list b2JointEdge* next; ///< the next joint edge in the body's joint list }; /// Joint definitions are used to construct joints. struct b2JointDef { b2JointDef() { type = e_unknownJoint; userData = NULL; bodyA = NULL; bodyB = NULL; collideConnected = false; } /// The joint type is set automatically for concrete joint types. b2JointType type; /// Use this to attach application specific data to your joints. void* userData; /// The first attached body. b2Body* bodyA; /// The second attached body. b2Body* bodyB; /// Set this flag to true if the attached bodies should collide. bool collideConnected; }; /// The base joint class. Joints are used to constraint two bodies together in /// various fashions. Some joints also feature limits and motors. class b2Joint { public: /// Get the type of the concrete joint. b2JointType GetType() const; /// Get the first body attached to this joint. b2Body* GetBodyA(); /// Get the second body attached to this joint. b2Body* GetBodyB(); /// Get the anchor point on bodyA in world coordinates. virtual b2Vec2 GetAnchorA() const = 0; /// Get the anchor point on bodyB in world coordinates. virtual b2Vec2 GetAnchorB() const = 0; /// Get the reaction force on bodyB at the joint anchor in Newtons. virtual b2Vec2 GetReactionForce(float32 inv_dt) const = 0; /// Get the reaction torque on bodyB in N*m. virtual float32 GetReactionTorque(float32 inv_dt) const = 0; /// Get the next joint the world joint list. b2Joint* GetNext(); const b2Joint* GetNext() const; /// Get the user data pointer. void* GetUserData() const; /// Set the user data pointer. void SetUserData(void* data); /// Short-cut function to determine if either body is inactive. bool IsActive() const; /// Get collide connected. /// Note: modifying the collide connect flag won't work correctly because /// the flag is only checked when fixture AABBs begin to overlap. bool GetCollideConnected() const; /// Dump this joint to the log file. virtual void Dump() { b2Log("// Dump is not supported for this joint type.\n"); } /// Shift the origin for any points stored in world coordinates. virtual void ShiftOrigin(const b2Vec2& newOrigin) { B2_NOT_USED(newOrigin); } protected: friend class b2World; friend class b2Body; friend class b2Island; friend class b2GearJoint; static b2Joint* Create(const b2JointDef* def, b2BlockAllocator* allocator); static void Destroy(b2Joint* joint, b2BlockAllocator* allocator); b2Joint(const b2JointDef* def); virtual ~b2Joint() {} virtual void InitVelocityConstraints(const b2SolverData& data) = 0; virtual void SolveVelocityConstraints(const b2SolverData& data) = 0; // This returns true if the position errors are within tolerance. virtual bool SolvePositionConstraints(const b2SolverData& data) = 0; b2JointType m_type; b2Joint* m_prev; b2Joint* m_next; b2JointEdge m_edgeA; b2JointEdge m_edgeB; b2Body* m_bodyA; b2Body* m_bodyB; int32 m_index; bool m_islandFlag; bool m_collideConnected; void* m_userData; }; inline b2JointType b2Joint::GetType() const { return m_type; } inline b2Body* b2Joint::GetBodyA() { return m_bodyA; } inline b2Body* b2Joint::GetBodyB() { return m_bodyB; } inline b2Joint* b2Joint::GetNext() { return m_next; } inline const b2Joint* b2Joint::GetNext() const { return m_next; } inline void* b2Joint::GetUserData() const { return m_userData; } inline void b2Joint::SetUserData(void* data) { m_userData = data; } inline bool b2Joint::GetCollideConnected() const { return m_collideConnected; } #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/Joints/b2MotorJoint.h ================================================ /* * Copyright (c) 2006-2012 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_MOTOR_JOINT_H #define B2_MOTOR_JOINT_H #include "b2Joint.h" /// Motor joint definition. struct b2MotorJointDef : public b2JointDef { b2MotorJointDef() { type = e_motorJoint; linearOffset.SetZero(); angularOffset = 0.0f; maxForce = 1.0f; maxTorque = 1.0f; correctionFactor = 0.3f; } /// Initialize the bodies and offsets using the current transforms. void Initialize(b2Body* bodyA, b2Body* bodyB); /// Position of bodyB minus the position of bodyA, in bodyA's frame, in meters. b2Vec2 linearOffset; /// The bodyB angle minus bodyA angle in radians. float32 angularOffset; /// The maximum motor force in N. float32 maxForce; /// The maximum motor torque in N-m. float32 maxTorque; /// Position correction factor in the range [0,1]. float32 correctionFactor; }; /// A motor joint is used to control the relative motion /// between two bodies. A typical usage is to control the movement /// of a dynamic body with respect to the ground. class b2MotorJoint : public b2Joint { public: b2Vec2 GetAnchorA() const; b2Vec2 GetAnchorB() const; b2Vec2 GetReactionForce(float32 inv_dt) const; float32 GetReactionTorque(float32 inv_dt) const; /// Set/get the target linear offset, in frame A, in meters. void SetLinearOffset(const b2Vec2& linearOffset); const b2Vec2& GetLinearOffset() const; /// Set/get the target angular offset, in radians. void SetAngularOffset(float32 angularOffset); float32 GetAngularOffset() const; /// Set the maximum friction force in N. void SetMaxForce(float32 force); /// Get the maximum friction force in N. float32 GetMaxForce() const; /// Set the maximum friction torque in N*m. void SetMaxTorque(float32 torque); /// Get the maximum friction torque in N*m. float32 GetMaxTorque() const; /// Set the position correction factor in the range [0,1]. void SetCorrectionFactor(float32 factor); /// Get the position correction factor in the range [0,1]. float32 GetCorrectionFactor() const; /// Dump to b2Log void Dump(); protected: friend class b2Joint; b2MotorJoint(const b2MotorJointDef* def); void InitVelocityConstraints(const b2SolverData& data); void SolveVelocityConstraints(const b2SolverData& data); bool SolvePositionConstraints(const b2SolverData& data); // Solver shared b2Vec2 m_linearOffset; float32 m_angularOffset; b2Vec2 m_linearImpulse; float32 m_angularImpulse; float32 m_maxForce; float32 m_maxTorque; float32 m_correctionFactor; // Solver temp int32 m_indexA; int32 m_indexB; b2Vec2 m_rA; b2Vec2 m_rB; b2Vec2 m_localCenterA; b2Vec2 m_localCenterB; b2Vec2 m_linearError; float32 m_angularError; float32 m_invMassA; float32 m_invMassB; float32 m_invIA; float32 m_invIB; b2Mat22 m_linearMass; float32 m_angularMass; }; #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/Joints/b2MouseJoint.h ================================================ /* * Copyright (c) 2006-2007 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_MOUSE_JOINT_H #define B2_MOUSE_JOINT_H #include "b2Joint.h" /// Mouse joint definition. This requires a world target point, /// tuning parameters, and the time step. struct b2MouseJointDef : public b2JointDef { b2MouseJointDef() { type = e_mouseJoint; target.Set(0.0f, 0.0f); maxForce = 0.0f; frequencyHz = 5.0f; dampingRatio = 0.7f; } /// The initial world target point. This is assumed /// to coincide with the body anchor initially. b2Vec2 target; /// The maximum constraint force that can be exerted /// to move the candidate body. Usually you will express /// as some multiple of the weight (multiplier * mass * gravity). float32 maxForce; /// The response speed. float32 frequencyHz; /// The damping ratio. 0 = no damping, 1 = critical damping. float32 dampingRatio; }; /// A mouse joint is used to make a point on a body track a /// specified world point. This a soft constraint with a maximum /// force. This allows the constraint to stretch and without /// applying huge forces. /// NOTE: this joint is not documented in the manual because it was /// developed to be used in the testbed. If you want to learn how to /// use the mouse joint, look at the testbed. class b2MouseJoint : public b2Joint { public: /// Implements b2Joint. b2Vec2 GetAnchorA() const; /// Implements b2Joint. b2Vec2 GetAnchorB() const; /// Implements b2Joint. b2Vec2 GetReactionForce(float32 inv_dt) const; /// Implements b2Joint. float32 GetReactionTorque(float32 inv_dt) const; /// Use this to update the target point. void SetTarget(const b2Vec2& target); const b2Vec2& GetTarget() const; /// Set/get the maximum force in Newtons. void SetMaxForce(float32 force); float32 GetMaxForce() const; /// Set/get the frequency in Hertz. void SetFrequency(float32 hz); float32 GetFrequency() const; /// Set/get the damping ratio (dimensionless). void SetDampingRatio(float32 ratio); float32 GetDampingRatio() const; /// The mouse joint does not support dumping. void Dump() { b2Log("Mouse joint dumping is not supported.\n"); } /// Implement b2Joint::ShiftOrigin void ShiftOrigin(const b2Vec2& newOrigin); protected: friend class b2Joint; b2MouseJoint(const b2MouseJointDef* def); void InitVelocityConstraints(const b2SolverData& data); void SolveVelocityConstraints(const b2SolverData& data); bool SolvePositionConstraints(const b2SolverData& data); b2Vec2 m_localAnchorB; b2Vec2 m_targetA; float32 m_frequencyHz; float32 m_dampingRatio; float32 m_beta; // Solver shared b2Vec2 m_impulse; float32 m_maxForce; float32 m_gamma; // Solver temp int32 m_indexA; int32 m_indexB; b2Vec2 m_rB; b2Vec2 m_localCenterB; float32 m_invMassB; float32 m_invIB; b2Mat22 m_mass; b2Vec2 m_C; }; #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/Joints/b2PrismaticJoint.h ================================================ /* * Copyright (c) 2006-2011 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_PRISMATIC_JOINT_H #define B2_PRISMATIC_JOINT_H #include "b2Joint.h" /// Prismatic joint definition. This requires defining a line of /// motion using an axis and an anchor point. The definition uses local /// anchor points and a local axis so that the initial configuration /// can violate the constraint slightly. The joint translation is zero /// when the local anchor points coincide in world space. Using local /// anchors and a local axis helps when saving and loading a game. struct b2PrismaticJointDef : public b2JointDef { b2PrismaticJointDef() { type = e_prismaticJoint; localAnchorA.SetZero(); localAnchorB.SetZero(); localAxisA.Set(1.0f, 0.0f); referenceAngle = 0.0f; enableLimit = false; lowerTranslation = 0.0f; upperTranslation = 0.0f; enableMotor = false; maxMotorForce = 0.0f; motorSpeed = 0.0f; } /// Initialize the bodies, anchors, axis, and reference angle using the world /// anchor and unit world axis. void Initialize(b2Body* bodyA, b2Body* bodyB, const b2Vec2& anchor, const b2Vec2& axis); /// The local anchor point relative to bodyA's origin. b2Vec2 localAnchorA; /// The local anchor point relative to bodyB's origin. b2Vec2 localAnchorB; /// The local translation unit axis in bodyA. b2Vec2 localAxisA; /// The constrained angle between the bodies: bodyB_angle - bodyA_angle. float32 referenceAngle; /// Enable/disable the joint limit. bool enableLimit; /// The lower translation limit, usually in meters. float32 lowerTranslation; /// The upper translation limit, usually in meters. float32 upperTranslation; /// Enable/disable the joint motor. bool enableMotor; /// The maximum motor torque, usually in N-m. float32 maxMotorForce; /// The desired motor speed in radians per second. float32 motorSpeed; }; /// A prismatic joint. This joint provides one degree of freedom: translation /// along an axis fixed in bodyA. Relative rotation is prevented. You can /// use a joint limit to restrict the range of motion and a joint motor to /// drive the motion or to model joint friction. class b2PrismaticJoint : public b2Joint { public: b2Vec2 GetAnchorA() const; b2Vec2 GetAnchorB() const; b2Vec2 GetReactionForce(float32 inv_dt) const; float32 GetReactionTorque(float32 inv_dt) const; /// The local anchor point relative to bodyA's origin. const b2Vec2& GetLocalAnchorA() const { return m_localAnchorA; } /// The local anchor point relative to bodyB's origin. const b2Vec2& GetLocalAnchorB() const { return m_localAnchorB; } /// The local joint axis relative to bodyA. const b2Vec2& GetLocalAxisA() const { return m_localXAxisA; } /// Get the reference angle. float32 GetReferenceAngle() const { return m_referenceAngle; } /// Get the current joint translation, usually in meters. float32 GetJointTranslation() const; /// Get the current joint translation speed, usually in meters per second. float32 GetJointSpeed() const; /// Is the joint limit enabled? bool IsLimitEnabled() const; /// Enable/disable the joint limit. void EnableLimit(bool flag); /// Get the lower joint limit, usually in meters. float32 GetLowerLimit() const; /// Get the upper joint limit, usually in meters. float32 GetUpperLimit() const; /// Set the joint limits, usually in meters. void SetLimits(float32 lower, float32 upper); /// Is the joint motor enabled? bool IsMotorEnabled() const; /// Enable/disable the joint motor. void EnableMotor(bool flag); /// Set the motor speed, usually in meters per second. void SetMotorSpeed(float32 speed); /// Get the motor speed, usually in meters per second. float32 GetMotorSpeed() const; /// Set the maximum motor force, usually in N. void SetMaxMotorForce(float32 force); float32 GetMaxMotorForce() const { return m_maxMotorForce; } /// Get the current motor force given the inverse time step, usually in N. float32 GetMotorForce(float32 inv_dt) const; /// Dump to b2Log void Dump(); protected: friend class b2Joint; friend class b2GearJoint; b2PrismaticJoint(const b2PrismaticJointDef* def); void InitVelocityConstraints(const b2SolverData& data); void SolveVelocityConstraints(const b2SolverData& data); bool SolvePositionConstraints(const b2SolverData& data); // Solver shared b2Vec2 m_localAnchorA; b2Vec2 m_localAnchorB; b2Vec2 m_localXAxisA; b2Vec2 m_localYAxisA; float32 m_referenceAngle; b2Vec3 m_impulse; float32 m_motorImpulse; float32 m_lowerTranslation; float32 m_upperTranslation; float32 m_maxMotorForce; float32 m_motorSpeed; bool m_enableLimit; bool m_enableMotor; b2LimitState m_limitState; // Solver temp int32 m_indexA; int32 m_indexB; b2Vec2 m_localCenterA; b2Vec2 m_localCenterB; float32 m_invMassA; float32 m_invMassB; float32 m_invIA; float32 m_invIB; b2Vec2 m_axis, m_perp; float32 m_s1, m_s2; float32 m_a1, m_a2; b2Mat33 m_K; float32 m_motorMass; }; inline float32 b2PrismaticJoint::GetMotorSpeed() const { return m_motorSpeed; } #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/Joints/b2PulleyJoint.h ================================================ /* * Copyright (c) 2006-2011 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_PULLEY_JOINT_H #define B2_PULLEY_JOINT_H #include "b2Joint.h" const float32 b2_minPulleyLength = 2.0f; /// Pulley joint definition. This requires two ground anchors, /// two dynamic body anchor points, and a pulley ratio. struct b2PulleyJointDef : public b2JointDef { b2PulleyJointDef() { type = e_pulleyJoint; groundAnchorA.Set(-1.0f, 1.0f); groundAnchorB.Set(1.0f, 1.0f); localAnchorA.Set(-1.0f, 0.0f); localAnchorB.Set(1.0f, 0.0f); lengthA = 0.0f; lengthB = 0.0f; ratio = 1.0f; collideConnected = true; } /// Initialize the bodies, anchors, lengths, max lengths, and ratio using the world anchors. void Initialize(b2Body* bodyA, b2Body* bodyB, const b2Vec2& groundAnchorA, const b2Vec2& groundAnchorB, const b2Vec2& anchorA, const b2Vec2& anchorB, float32 ratio); /// The first ground anchor in world coordinates. This point never moves. b2Vec2 groundAnchorA; /// The second ground anchor in world coordinates. This point never moves. b2Vec2 groundAnchorB; /// The local anchor point relative to bodyA's origin. b2Vec2 localAnchorA; /// The local anchor point relative to bodyB's origin. b2Vec2 localAnchorB; /// The a reference length for the segment attached to bodyA. float32 lengthA; /// The a reference length for the segment attached to bodyB. float32 lengthB; /// The pulley ratio, used to simulate a block-and-tackle. float32 ratio; }; /// The pulley joint is connected to two bodies and two fixed ground points. /// The pulley supports a ratio such that: /// length1 + ratio * length2 <= constant /// Yes, the force transmitted is scaled by the ratio. /// Warning: the pulley joint can get a bit squirrelly by itself. They often /// work better when combined with prismatic joints. You should also cover the /// the anchor points with static shapes to prevent one side from going to /// zero length. class b2PulleyJoint : public b2Joint { public: b2Vec2 GetAnchorA() const; b2Vec2 GetAnchorB() const; b2Vec2 GetReactionForce(float32 inv_dt) const; float32 GetReactionTorque(float32 inv_dt) const; /// Get the first ground anchor. b2Vec2 GetGroundAnchorA() const; /// Get the second ground anchor. b2Vec2 GetGroundAnchorB() const; /// Get the current length of the segment attached to bodyA. float32 GetLengthA() const; /// Get the current length of the segment attached to bodyB. float32 GetLengthB() const; /// Get the pulley ratio. float32 GetRatio() const; /// Get the current length of the segment attached to bodyA. float32 GetCurrentLengthA() const; /// Get the current length of the segment attached to bodyB. float32 GetCurrentLengthB() const; /// Dump joint to dmLog void Dump(); /// Implement b2Joint::ShiftOrigin void ShiftOrigin(const b2Vec2& newOrigin); protected: friend class b2Joint; b2PulleyJoint(const b2PulleyJointDef* data); void InitVelocityConstraints(const b2SolverData& data); void SolveVelocityConstraints(const b2SolverData& data); bool SolvePositionConstraints(const b2SolverData& data); b2Vec2 m_groundAnchorA; b2Vec2 m_groundAnchorB; float32 m_lengthA; float32 m_lengthB; // Solver shared b2Vec2 m_localAnchorA; b2Vec2 m_localAnchorB; float32 m_constant; float32 m_ratio; float32 m_impulse; // Solver temp int32 m_indexA; int32 m_indexB; b2Vec2 m_uA; b2Vec2 m_uB; b2Vec2 m_rA; b2Vec2 m_rB; b2Vec2 m_localCenterA; b2Vec2 m_localCenterB; float32 m_invMassA; float32 m_invMassB; float32 m_invIA; float32 m_invIB; float32 m_mass; }; #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/Joints/b2RevoluteJoint.h ================================================ /* * Copyright (c) 2006-2011 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_REVOLUTE_JOINT_H #define B2_REVOLUTE_JOINT_H #include "b2Joint.h" /// Revolute joint definition. This requires defining an /// anchor point where the bodies are joined. The definition /// uses local anchor points so that the initial configuration /// can violate the constraint slightly. You also need to /// specify the initial relative angle for joint limits. This /// helps when saving and loading a game. /// The local anchor points are measured from the body's origin /// rather than the center of mass because: /// 1. you might not know where the center of mass will be. /// 2. if you add/remove shapes from a body and recompute the mass, /// the joints will be broken. struct b2RevoluteJointDef : public b2JointDef { b2RevoluteJointDef() { type = e_revoluteJoint; localAnchorA.Set(0.0f, 0.0f); localAnchorB.Set(0.0f, 0.0f); referenceAngle = 0.0f; lowerAngle = 0.0f; upperAngle = 0.0f; maxMotorTorque = 0.0f; motorSpeed = 0.0f; enableLimit = false; enableMotor = false; } /// Initialize the bodies, anchors, and reference angle using a world /// anchor point. void Initialize(b2Body* bodyA, b2Body* bodyB, const b2Vec2& anchor); /// The local anchor point relative to bodyA's origin. b2Vec2 localAnchorA; /// The local anchor point relative to bodyB's origin. b2Vec2 localAnchorB; /// The bodyB angle minus bodyA angle in the reference state (radians). float32 referenceAngle; /// A flag to enable joint limits. bool enableLimit; /// The lower angle for the joint limit (radians). float32 lowerAngle; /// The upper angle for the joint limit (radians). float32 upperAngle; /// A flag to enable the joint motor. bool enableMotor; /// The desired motor speed. Usually in radians per second. float32 motorSpeed; /// The maximum motor torque used to achieve the desired motor speed. /// Usually in N-m. float32 maxMotorTorque; }; /// A revolute joint constrains two bodies to share a common point while they /// are free to rotate about the point. The relative rotation about the shared /// point is the joint angle. You can limit the relative rotation with /// a joint limit that specifies a lower and upper angle. You can use a motor /// to drive the relative rotation about the shared point. A maximum motor torque /// is provided so that infinite forces are not generated. class b2RevoluteJoint : public b2Joint { public: b2Vec2 GetAnchorA() const; b2Vec2 GetAnchorB() const; /// The local anchor point relative to bodyA's origin. const b2Vec2& GetLocalAnchorA() const { return m_localAnchorA; } /// The local anchor point relative to bodyB's origin. const b2Vec2& GetLocalAnchorB() const { return m_localAnchorB; } /// Get the reference angle. float32 GetReferenceAngle() const { return m_referenceAngle; } /// Get the current joint angle in radians. float32 GetJointAngle() const; /// Get the current joint angle speed in radians per second. float32 GetJointSpeed() const; /// Is the joint limit enabled? bool IsLimitEnabled() const; /// Enable/disable the joint limit. void EnableLimit(bool flag); /// Get the lower joint limit in radians. float32 GetLowerLimit() const; /// Get the upper joint limit in radians. float32 GetUpperLimit() const; /// Set the joint limits in radians. void SetLimits(float32 lower, float32 upper); /// Is the joint motor enabled? bool IsMotorEnabled() const; /// Enable/disable the joint motor. void EnableMotor(bool flag); /// Set the motor speed in radians per second. void SetMotorSpeed(float32 speed); /// Get the motor speed in radians per second. float32 GetMotorSpeed() const; /// Set the maximum motor torque, usually in N-m. void SetMaxMotorTorque(float32 torque); float32 GetMaxMotorTorque() const { return m_maxMotorTorque; } /// Get the reaction force given the inverse time step. /// Unit is N. b2Vec2 GetReactionForce(float32 inv_dt) const; /// Get the reaction torque due to the joint limit given the inverse time step. /// Unit is N*m. float32 GetReactionTorque(float32 inv_dt) const; /// Get the current motor torque given the inverse time step. /// Unit is N*m. float32 GetMotorTorque(float32 inv_dt) const; /// Dump to b2Log. void Dump(); protected: friend class b2Joint; friend class b2GearJoint; b2RevoluteJoint(const b2RevoluteJointDef* def); void InitVelocityConstraints(const b2SolverData& data); void SolveVelocityConstraints(const b2SolverData& data); bool SolvePositionConstraints(const b2SolverData& data); // Solver shared b2Vec2 m_localAnchorA; b2Vec2 m_localAnchorB; b2Vec3 m_impulse; float32 m_motorImpulse; bool m_enableMotor; float32 m_maxMotorTorque; float32 m_motorSpeed; bool m_enableLimit; float32 m_referenceAngle; float32 m_lowerAngle; float32 m_upperAngle; // Solver temp int32 m_indexA; int32 m_indexB; b2Vec2 m_rA; b2Vec2 m_rB; b2Vec2 m_localCenterA; b2Vec2 m_localCenterB; float32 m_invMassA; float32 m_invMassB; float32 m_invIA; float32 m_invIB; b2Mat33 m_mass; // effective mass for point-to-point constraint. float32 m_motorMass; // effective mass for motor/limit angular constraint. b2LimitState m_limitState; }; inline float32 b2RevoluteJoint::GetMotorSpeed() const { return m_motorSpeed; } #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/Joints/b2RopeJoint.h ================================================ /* * Copyright (c) 2006-2011 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_ROPE_JOINT_H #define B2_ROPE_JOINT_H #include "b2Joint.h" /// Rope joint definition. This requires two body anchor points and /// a maximum lengths. /// Note: by default the connected objects will not collide. /// see collideConnected in b2JointDef. struct b2RopeJointDef : public b2JointDef { b2RopeJointDef() { type = e_ropeJoint; localAnchorA.Set(-1.0f, 0.0f); localAnchorB.Set(1.0f, 0.0f); maxLength = 0.0f; } /// The local anchor point relative to bodyA's origin. b2Vec2 localAnchorA; /// The local anchor point relative to bodyB's origin. b2Vec2 localAnchorB; /// The maximum length of the rope. /// Warning: this must be larger than b2_linearSlop or /// the joint will have no effect. float32 maxLength; }; /// A rope joint enforces a maximum distance between two points /// on two bodies. It has no other effect. /// Warning: if you attempt to change the maximum length during /// the simulation you will get some non-physical behavior. /// A model that would allow you to dynamically modify the length /// would have some sponginess, so I chose not to implement it /// that way. See b2DistanceJoint if you want to dynamically /// control length. class b2RopeJoint : public b2Joint { public: b2Vec2 GetAnchorA() const; b2Vec2 GetAnchorB() const; b2Vec2 GetReactionForce(float32 inv_dt) const; float32 GetReactionTorque(float32 inv_dt) const; /// The local anchor point relative to bodyA's origin. const b2Vec2& GetLocalAnchorA() const { return m_localAnchorA; } /// The local anchor point relative to bodyB's origin. const b2Vec2& GetLocalAnchorB() const { return m_localAnchorB; } /// Set/Get the maximum length of the rope. void SetMaxLength(float32 length) { m_maxLength = length; } float32 GetMaxLength() const; b2LimitState GetLimitState() const; /// Dump joint to dmLog void Dump(); protected: friend class b2Joint; b2RopeJoint(const b2RopeJointDef* data); void InitVelocityConstraints(const b2SolverData& data); void SolveVelocityConstraints(const b2SolverData& data); bool SolvePositionConstraints(const b2SolverData& data); // Solver shared b2Vec2 m_localAnchorA; b2Vec2 m_localAnchorB; float32 m_maxLength; float32 m_length; float32 m_impulse; // Solver temp int32 m_indexA; int32 m_indexB; b2Vec2 m_u; b2Vec2 m_rA; b2Vec2 m_rB; b2Vec2 m_localCenterA; b2Vec2 m_localCenterB; float32 m_invMassA; float32 m_invMassB; float32 m_invIA; float32 m_invIB; float32 m_mass; b2LimitState m_state; }; #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/Joints/b2WeldJoint.h ================================================ /* * Copyright (c) 2006-2011 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_WELD_JOINT_H #define B2_WELD_JOINT_H #include "b2Joint.h" /// Weld joint definition. You need to specify local anchor points /// where they are attached and the relative body angle. The position /// of the anchor points is important for computing the reaction torque. struct b2WeldJointDef : public b2JointDef { b2WeldJointDef() { type = e_weldJoint; localAnchorA.Set(0.0f, 0.0f); localAnchorB.Set(0.0f, 0.0f); referenceAngle = 0.0f; frequencyHz = 0.0f; dampingRatio = 0.0f; } /// Initialize the bodies, anchors, and reference angle using a world /// anchor point. void Initialize(b2Body* bodyA, b2Body* bodyB, const b2Vec2& anchor); /// The local anchor point relative to bodyA's origin. b2Vec2 localAnchorA; /// The local anchor point relative to bodyB's origin. b2Vec2 localAnchorB; /// The bodyB angle minus bodyA angle in the reference state (radians). float32 referenceAngle; /// The mass-spring-damper frequency in Hertz. Rotation only. /// Disable softness with a value of 0. float32 frequencyHz; /// The damping ratio. 0 = no damping, 1 = critical damping. float32 dampingRatio; }; /// A weld joint essentially glues two bodies together. A weld joint may /// distort somewhat because the island constraint solver is approximate. class b2WeldJoint : public b2Joint { public: b2Vec2 GetAnchorA() const; b2Vec2 GetAnchorB() const; b2Vec2 GetReactionForce(float32 inv_dt) const; float32 GetReactionTorque(float32 inv_dt) const; /// The local anchor point relative to bodyA's origin. const b2Vec2& GetLocalAnchorA() const { return m_localAnchorA; } /// The local anchor point relative to bodyB's origin. const b2Vec2& GetLocalAnchorB() const { return m_localAnchorB; } /// Get the reference angle. float32 GetReferenceAngle() const { return m_referenceAngle; } /// Set/get frequency in Hz. void SetFrequency(float32 hz) { m_frequencyHz = hz; } float32 GetFrequency() const { return m_frequencyHz; } /// Set/get damping ratio. void SetDampingRatio(float32 ratio) { m_dampingRatio = ratio; } float32 GetDampingRatio() const { return m_dampingRatio; } /// Dump to b2Log void Dump(); protected: friend class b2Joint; b2WeldJoint(const b2WeldJointDef* def); void InitVelocityConstraints(const b2SolverData& data); void SolveVelocityConstraints(const b2SolverData& data); bool SolvePositionConstraints(const b2SolverData& data); float32 m_frequencyHz; float32 m_dampingRatio; float32 m_bias; // Solver shared b2Vec2 m_localAnchorA; b2Vec2 m_localAnchorB; float32 m_referenceAngle; float32 m_gamma; b2Vec3 m_impulse; // Solver temp int32 m_indexA; int32 m_indexB; b2Vec2 m_rA; b2Vec2 m_rB; b2Vec2 m_localCenterA; b2Vec2 m_localCenterB; float32 m_invMassA; float32 m_invMassB; float32 m_invIA; float32 m_invIB; b2Mat33 m_mass; }; #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/Joints/b2WheelJoint.h ================================================ /* * Copyright (c) 2006-2011 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_WHEEL_JOINT_H #define B2_WHEEL_JOINT_H #include "b2Joint.h" /// Wheel joint definition. This requires defining a line of /// motion using an axis and an anchor point. The definition uses local /// anchor points and a local axis so that the initial configuration /// can violate the constraint slightly. The joint translation is zero /// when the local anchor points coincide in world space. Using local /// anchors and a local axis helps when saving and loading a game. struct b2WheelJointDef : public b2JointDef { b2WheelJointDef() { type = e_wheelJoint; localAnchorA.SetZero(); localAnchorB.SetZero(); localAxisA.Set(1.0f, 0.0f); enableMotor = false; maxMotorTorque = 0.0f; motorSpeed = 0.0f; frequencyHz = 2.0f; dampingRatio = 0.7f; } /// Initialize the bodies, anchors, axis, and reference angle using the world /// anchor and world axis. void Initialize(b2Body* bodyA, b2Body* bodyB, const b2Vec2& anchor, const b2Vec2& axis); /// The local anchor point relative to bodyA's origin. b2Vec2 localAnchorA; /// The local anchor point relative to bodyB's origin. b2Vec2 localAnchorB; /// The local translation axis in bodyA. b2Vec2 localAxisA; /// Enable/disable the joint motor. bool enableMotor; /// The maximum motor torque, usually in N-m. float32 maxMotorTorque; /// The desired motor speed in radians per second. float32 motorSpeed; /// Suspension frequency, zero indicates no suspension float32 frequencyHz; /// Suspension damping ratio, one indicates critical damping float32 dampingRatio; }; /// A wheel joint. This joint provides two degrees of freedom: translation /// along an axis fixed in bodyA and rotation in the plane. You can use a /// joint limit to restrict the range of motion and a joint motor to drive /// the rotation or to model rotational friction. /// This joint is designed for vehicle suspensions. class b2WheelJoint : public b2Joint { public: b2Vec2 GetAnchorA() const; b2Vec2 GetAnchorB() const; b2Vec2 GetReactionForce(float32 inv_dt) const; float32 GetReactionTorque(float32 inv_dt) const; /// The local anchor point relative to bodyA's origin. const b2Vec2& GetLocalAnchorA() const { return m_localAnchorA; } /// The local anchor point relative to bodyB's origin. const b2Vec2& GetLocalAnchorB() const { return m_localAnchorB; } /// The local joint axis relative to bodyA. const b2Vec2& GetLocalAxisA() const { return m_localXAxisA; } /// Get the current joint translation, usually in meters. float32 GetJointTranslation() const; /// Get the current joint translation speed, usually in meters per second. float32 GetJointSpeed() const; /// Is the joint motor enabled? bool IsMotorEnabled() const; /// Enable/disable the joint motor. void EnableMotor(bool flag); /// Set the motor speed, usually in radians per second. void SetMotorSpeed(float32 speed); /// Get the motor speed, usually in radians per second. float32 GetMotorSpeed() const; /// Set/Get the maximum motor force, usually in N-m. void SetMaxMotorTorque(float32 torque); float32 GetMaxMotorTorque() const; /// Get the current motor torque given the inverse time step, usually in N-m. float32 GetMotorTorque(float32 inv_dt) const; /// Set/Get the spring frequency in hertz. Setting the frequency to zero disables the spring. void SetSpringFrequencyHz(float32 hz); float32 GetSpringFrequencyHz() const; /// Set/Get the spring damping ratio void SetSpringDampingRatio(float32 ratio); float32 GetSpringDampingRatio() const; /// Dump to b2Log void Dump(); protected: friend class b2Joint; b2WheelJoint(const b2WheelJointDef* def); void InitVelocityConstraints(const b2SolverData& data); void SolveVelocityConstraints(const b2SolverData& data); bool SolvePositionConstraints(const b2SolverData& data); float32 m_frequencyHz; float32 m_dampingRatio; // Solver shared b2Vec2 m_localAnchorA; b2Vec2 m_localAnchorB; b2Vec2 m_localXAxisA; b2Vec2 m_localYAxisA; float32 m_impulse; float32 m_motorImpulse; float32 m_springImpulse; float32 m_maxMotorTorque; float32 m_motorSpeed; bool m_enableMotor; // Solver temp int32 m_indexA; int32 m_indexB; b2Vec2 m_localCenterA; b2Vec2 m_localCenterB; float32 m_invMassA; float32 m_invMassB; float32 m_invIA; float32 m_invIB; b2Vec2 m_ax, m_ay; float32 m_sAx, m_sBx; float32 m_sAy, m_sBy; float32 m_mass; float32 m_motorMass; float32 m_springMass; float32 m_bias; float32 m_gamma; }; inline float32 b2WheelJoint::GetMotorSpeed() const { return m_motorSpeed; } inline float32 b2WheelJoint::GetMaxMotorTorque() const { return m_maxMotorTorque; } inline void b2WheelJoint::SetSpringFrequencyHz(float32 hz) { m_frequencyHz = hz; } inline float32 b2WheelJoint::GetSpringFrequencyHz() const { return m_frequencyHz; } inline void b2WheelJoint::SetSpringDampingRatio(float32 ratio) { m_dampingRatio = ratio; } inline float32 b2WheelJoint::GetSpringDampingRatio() const { return m_dampingRatio; } #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/b2Body.h ================================================ /* * Copyright (c) 2006-2011 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_BODY_H #define B2_BODY_H #include "../Common/b2Math.h" #include "../Collision/Shapes/b2Shape.h" #include class b2Fixture; class b2Joint; class b2Contact; class b2Controller; class b2World; struct b2FixtureDef; struct b2JointEdge; struct b2ContactEdge; /// The body type. /// static: zero mass, zero velocity, may be manually moved /// kinematic: zero mass, non-zero velocity set by user, moved by solver /// dynamic: positive mass, non-zero velocity determined by forces, moved by solver enum b2BodyType { b2_staticBody = 0, b2_kinematicBody, b2_dynamicBody // TODO_ERIN //b2_bulletBody, }; /// A body definition holds all the data needed to construct a rigid body. /// You can safely re-use body definitions. Shapes are added to a body after construction. struct b2BodyDef { /// This constructor sets the body definition default values. b2BodyDef() { userData = NULL; position.Set(0.0f, 0.0f); angle = 0.0f; linearVelocity.Set(0.0f, 0.0f); angularVelocity = 0.0f; linearDamping = 0.0f; angularDamping = 0.0f; allowSleep = true; awake = true; fixedRotation = false; bullet = false; type = b2_staticBody; active = true; gravityScale = 1.0f; } /// The body type: static, kinematic, or dynamic. /// Note: if a dynamic body would have zero mass, the mass is set to one. b2BodyType type; /// The world position of the body. Avoid creating bodies at the origin /// since this can lead to many overlapping shapes. b2Vec2 position; /// The world angle of the body in radians. float32 angle; /// The linear velocity of the body's origin in world co-ordinates. b2Vec2 linearVelocity; /// The angular velocity of the body. float32 angularVelocity; /// Linear damping is use to reduce the linear velocity. The damping parameter /// can be larger than 1.0f but the damping effect becomes sensitive to the /// time step when the damping parameter is large. float32 linearDamping; /// Angular damping is use to reduce the angular velocity. The damping parameter /// can be larger than 1.0f but the damping effect becomes sensitive to the /// time step when the damping parameter is large. float32 angularDamping; /// Set this flag to false if this body should never fall asleep. Note that /// this increases CPU usage. bool allowSleep; /// Is this body initially awake or sleeping? bool awake; /// Should this body be prevented from rotating? Useful for characters. bool fixedRotation; /// Is this a fast moving body that should be prevented from tunneling through /// other moving bodies? Note that all bodies are prevented from tunneling through /// kinematic and static bodies. This setting is only considered on dynamic bodies. /// @warning You should use this flag sparingly since it increases processing time. bool bullet; /// Does this body start out active? bool active; /// Use this to store application specific body data. void* userData; /// Scale the gravity applied to this body. float32 gravityScale; }; /// A rigid body. These are created via b2World::CreateBody. class b2Body { public: /// Creates a fixture and attach it to this body. Use this function if you need /// to set some fixture parameters, like friction. Otherwise you can create the /// fixture directly from a shape. /// If the density is non-zero, this function automatically updates the mass of the body. /// Contacts are not created until the next time step. /// @param def the fixture definition. /// @warning This function is locked during callbacks. b2Fixture* CreateFixture(const b2FixtureDef* def); /// Creates a fixture from a shape and attach it to this body. /// This is a convenience function. Use b2FixtureDef if you need to set parameters /// like friction, restitution, user data, or filtering. /// If the density is non-zero, this function automatically updates the mass of the body. /// @param shape the shape to be cloned. /// @param density the shape density (set to zero for static bodies). /// @warning This function is locked during callbacks. b2Fixture* CreateFixture(const b2Shape* shape, float32 density); /// Destroy a fixture. This removes the fixture from the broad-phase and /// destroys all contacts associated with this fixture. This will /// automatically adjust the mass of the body if the body is dynamic and the /// fixture has positive density. /// All fixtures attached to a body are implicitly destroyed when the body is destroyed. /// @param fixture the fixture to be removed. /// @warning This function is locked during callbacks. void DestroyFixture(b2Fixture* fixture); /// Set the position of the body's origin and rotation. /// Manipulating a body's transform may cause non-physical behavior. /// Note: contacts are updated on the next call to b2World::Step. /// @param position the world position of the body's local origin. /// @param angle the world rotation in radians. void SetTransform(const b2Vec2& position, float32 angle); /// Get the body transform for the body's origin. /// @return the world transform of the body's origin. const b2Transform& GetTransform() const; /// Get the world body origin position. /// @return the world position of the body's origin. const b2Vec2& GetPosition() const; /// Get the angle in radians. /// @return the current world rotation angle in radians. float32 GetAngle() const; /// Get the world position of the center of mass. const b2Vec2& GetWorldCenter() const; /// Get the local position of the center of mass. const b2Vec2& GetLocalCenter() const; /// Set the linear velocity of the center of mass. /// @param v the new linear velocity of the center of mass. void SetLinearVelocity(const b2Vec2& v); /// Get the linear velocity of the center of mass. /// @return the linear velocity of the center of mass. const b2Vec2& GetLinearVelocity() const; /// Set the angular velocity. /// @param omega the new angular velocity in radians/second. void SetAngularVelocity(float32 omega); /// Get the angular velocity. /// @return the angular velocity in radians/second. float32 GetAngularVelocity() const; /// Apply a force at a world point. If the force is not /// applied at the center of mass, it will generate a torque and /// affect the angular velocity. This wakes up the body. /// @param force the world force vector, usually in Newtons (N). /// @param point the world position of the point of application. /// @param wake also wake up the body void ApplyForce(const b2Vec2& force, const b2Vec2& point, bool wake); /// Apply a force to the center of mass. This wakes up the body. /// @param force the world force vector, usually in Newtons (N). /// @param wake also wake up the body void ApplyForceToCenter(const b2Vec2& force, bool wake); /// Apply a torque. This affects the angular velocity /// without affecting the linear velocity of the center of mass. /// This wakes up the body. /// @param torque about the z-axis (out of the screen), usually in N-m. /// @param wake also wake up the body void ApplyTorque(float32 torque, bool wake); /// Apply an impulse at a point. This immediately modifies the velocity. /// It also modifies the angular velocity if the point of application /// is not at the center of mass. This wakes up the body. /// @param impulse the world impulse vector, usually in N-seconds or kg-m/s. /// @param point the world position of the point of application. /// @param wake also wake up the body void ApplyLinearImpulse(const b2Vec2& impulse, const b2Vec2& point, bool wake); /// Apply an angular impulse. /// @param impulse the angular impulse in units of kg*m*m/s /// @param wake also wake up the body void ApplyAngularImpulse(float32 impulse, bool wake); /// Get the total mass of the body. /// @return the mass, usually in kilograms (kg). float32 GetMass() const; /// Get the rotational inertia of the body about the local origin. /// @return the rotational inertia, usually in kg-m^2. float32 GetInertia() const; /// Get the mass data of the body. /// @return a struct containing the mass, inertia and center of the body. void GetMassData(b2MassData* data) const; /// Set the mass properties to override the mass properties of the fixtures. /// Note that this changes the center of mass position. /// Note that creating or destroying fixtures can also alter the mass. /// This function has no effect if the body isn't dynamic. /// @param massData the mass properties. void SetMassData(const b2MassData* data); /// This resets the mass properties to the sum of the mass properties of the fixtures. /// This normally does not need to be called unless you called SetMassData to override /// the mass and you later want to reset the mass. void ResetMassData(); /// Get the world coordinates of a point given the local coordinates. /// @param localPoint a point on the body measured relative the the body's origin. /// @return the same point expressed in world coordinates. b2Vec2 GetWorldPoint(const b2Vec2& localPoint) const; /// Get the world coordinates of a vector given the local coordinates. /// @param localVector a vector fixed in the body. /// @return the same vector expressed in world coordinates. b2Vec2 GetWorldVector(const b2Vec2& localVector) const; /// Gets a local point relative to the body's origin given a world point. /// @param a point in world coordinates. /// @return the corresponding local point relative to the body's origin. b2Vec2 GetLocalPoint(const b2Vec2& worldPoint) const; /// Gets a local vector given a world vector. /// @param a vector in world coordinates. /// @return the corresponding local vector. b2Vec2 GetLocalVector(const b2Vec2& worldVector) const; /// Get the world linear velocity of a world point attached to this body. /// @param a point in world coordinates. /// @return the world velocity of a point. b2Vec2 GetLinearVelocityFromWorldPoint(const b2Vec2& worldPoint) const; /// Get the world velocity of a local point. /// @param a point in local coordinates. /// @return the world velocity of a point. b2Vec2 GetLinearVelocityFromLocalPoint(const b2Vec2& localPoint) const; /// Get the linear damping of the body. float32 GetLinearDamping() const; /// Set the linear damping of the body. void SetLinearDamping(float32 linearDamping); /// Get the angular damping of the body. float32 GetAngularDamping() const; /// Set the angular damping of the body. void SetAngularDamping(float32 angularDamping); /// Get the gravity scale of the body. float32 GetGravityScale() const; /// Set the gravity scale of the body. void SetGravityScale(float32 scale); /// Set the type of this body. This may alter the mass and velocity. void SetType(b2BodyType type); /// Get the type of this body. b2BodyType GetType() const; /// Should this body be treated like a bullet for continuous collision detection? void SetBullet(bool flag); /// Is this body treated like a bullet for continuous collision detection? bool IsBullet() const; /// You can disable sleeping on this body. If you disable sleeping, the /// body will be woken. void SetSleepingAllowed(bool flag); /// Is this body allowed to sleep bool IsSleepingAllowed() const; /// Set the sleep state of the body. A sleeping body has very /// low CPU cost. /// @param flag set to true to wake the body, false to put it to sleep. void SetAwake(bool flag); /// Get the sleeping state of this body. /// @return true if the body is awake. bool IsAwake() const; /// Set the active state of the body. An inactive body is not /// simulated and cannot be collided with or woken up. /// If you pass a flag of true, all fixtures will be added to the /// broad-phase. /// If you pass a flag of false, all fixtures will be removed from /// the broad-phase and all contacts will be destroyed. /// Fixtures and joints are otherwise unaffected. You may continue /// to create/destroy fixtures and joints on inactive bodies. /// Fixtures on an inactive body are implicitly inactive and will /// not participate in collisions, ray-casts, or queries. /// Joints connected to an inactive body are implicitly inactive. /// An inactive body is still owned by a b2World object and remains /// in the body list. void SetActive(bool flag); /// Get the active state of the body. bool IsActive() const; /// Set this body to have fixed rotation. This causes the mass /// to be reset. void SetFixedRotation(bool flag); /// Does this body have fixed rotation? bool IsFixedRotation() const; /// Get the list of all fixtures attached to this body. b2Fixture* GetFixtureList(); const b2Fixture* GetFixtureList() const; /// Get the list of all joints attached to this body. b2JointEdge* GetJointList(); const b2JointEdge* GetJointList() const; /// Get the list of all contacts attached to this body. /// @warning this list changes during the time step and you may /// miss some collisions if you don't use b2ContactListener. b2ContactEdge* GetContactList(); const b2ContactEdge* GetContactList() const; /// Get the next body in the world's body list. b2Body* GetNext(); const b2Body* GetNext() const; /// Get the user data pointer that was provided in the body definition. void* GetUserData() const; /// Set the user data. Use this to store your application specific data. void SetUserData(void* data); /// Get the parent world of this body. b2World* GetWorld(); const b2World* GetWorld() const; /// Dump this body to a log file void Dump(); private: friend class b2World; friend class b2Island; friend class b2ContactManager; friend class b2ContactSolver; friend class b2Contact; friend class b2DistanceJoint; friend class b2FrictionJoint; friend class b2GearJoint; friend class b2MotorJoint; friend class b2MouseJoint; friend class b2PrismaticJoint; friend class b2PulleyJoint; friend class b2RevoluteJoint; friend class b2RopeJoint; friend class b2WeldJoint; friend class b2WheelJoint; // m_flags enum { e_islandFlag = 0x0001, e_awakeFlag = 0x0002, e_autoSleepFlag = 0x0004, e_bulletFlag = 0x0008, e_fixedRotationFlag = 0x0010, e_activeFlag = 0x0020, e_toiFlag = 0x0040 }; b2Body(const b2BodyDef* bd, b2World* world); ~b2Body(); void SynchronizeFixtures(); void SynchronizeTransform(); // This is used to prevent connected bodies from colliding. // It may lie, depending on the collideConnected flag. bool ShouldCollide(const b2Body* other) const; void Advance(float32 t); b2BodyType m_type; uint16 m_flags; int32 m_islandIndex; b2Transform m_xf; // the body origin transform b2Sweep m_sweep; // the swept motion for CCD b2Vec2 m_linearVelocity; float32 m_angularVelocity; b2Vec2 m_force; float32 m_torque; b2World* m_world; b2Body* m_prev; b2Body* m_next; b2Fixture* m_fixtureList; int32 m_fixtureCount; b2JointEdge* m_jointList; b2ContactEdge* m_contactList; float32 m_mass, m_invMass; // Rotational inertia about the center of mass. float32 m_I, m_invI; float32 m_linearDamping; float32 m_angularDamping; float32 m_gravityScale; float32 m_sleepTime; void* m_userData; }; inline b2BodyType b2Body::GetType() const { return m_type; } inline const b2Transform& b2Body::GetTransform() const { return m_xf; } inline const b2Vec2& b2Body::GetPosition() const { return m_xf.p; } inline float32 b2Body::GetAngle() const { return m_sweep.a; } inline const b2Vec2& b2Body::GetWorldCenter() const { return m_sweep.c; } inline const b2Vec2& b2Body::GetLocalCenter() const { return m_sweep.localCenter; } inline void b2Body::SetLinearVelocity(const b2Vec2& v) { if (m_type == b2_staticBody) { return; } if (b2Dot(v,v) > 0.0f) { SetAwake(true); } m_linearVelocity = v; } inline const b2Vec2& b2Body::GetLinearVelocity() const { return m_linearVelocity; } inline void b2Body::SetAngularVelocity(float32 w) { if (m_type == b2_staticBody) { return; } if (w * w > 0.0f) { SetAwake(true); } m_angularVelocity = w; } inline float32 b2Body::GetAngularVelocity() const { return m_angularVelocity; } inline float32 b2Body::GetMass() const { return m_mass; } inline float32 b2Body::GetInertia() const { return m_I + m_mass * b2Dot(m_sweep.localCenter, m_sweep.localCenter); } inline void b2Body::GetMassData(b2MassData* data) const { data->mass = m_mass; data->I = m_I + m_mass * b2Dot(m_sweep.localCenter, m_sweep.localCenter); data->center = m_sweep.localCenter; } inline b2Vec2 b2Body::GetWorldPoint(const b2Vec2& localPoint) const { return b2Mul(m_xf, localPoint); } inline b2Vec2 b2Body::GetWorldVector(const b2Vec2& localVector) const { return b2Mul(m_xf.q, localVector); } inline b2Vec2 b2Body::GetLocalPoint(const b2Vec2& worldPoint) const { return b2MulT(m_xf, worldPoint); } inline b2Vec2 b2Body::GetLocalVector(const b2Vec2& worldVector) const { return b2MulT(m_xf.q, worldVector); } inline b2Vec2 b2Body::GetLinearVelocityFromWorldPoint(const b2Vec2& worldPoint) const { return m_linearVelocity + b2Cross(m_angularVelocity, worldPoint - m_sweep.c); } inline b2Vec2 b2Body::GetLinearVelocityFromLocalPoint(const b2Vec2& localPoint) const { return GetLinearVelocityFromWorldPoint(GetWorldPoint(localPoint)); } inline float32 b2Body::GetLinearDamping() const { return m_linearDamping; } inline void b2Body::SetLinearDamping(float32 linearDamping) { m_linearDamping = linearDamping; } inline float32 b2Body::GetAngularDamping() const { return m_angularDamping; } inline void b2Body::SetAngularDamping(float32 angularDamping) { m_angularDamping = angularDamping; } inline float32 b2Body::GetGravityScale() const { return m_gravityScale; } inline void b2Body::SetGravityScale(float32 scale) { m_gravityScale = scale; } inline void b2Body::SetBullet(bool flag) { if (flag) { m_flags |= e_bulletFlag; } else { m_flags &= ~e_bulletFlag; } } inline bool b2Body::IsBullet() const { return (m_flags & e_bulletFlag) == e_bulletFlag; } inline void b2Body::SetAwake(bool flag) { if (flag) { if ((m_flags & e_awakeFlag) == 0) { m_flags |= e_awakeFlag; m_sleepTime = 0.0f; } } else { m_flags &= ~e_awakeFlag; m_sleepTime = 0.0f; m_linearVelocity.SetZero(); m_angularVelocity = 0.0f; m_force.SetZero(); m_torque = 0.0f; } } inline bool b2Body::IsAwake() const { return (m_flags & e_awakeFlag) == e_awakeFlag; } inline bool b2Body::IsActive() const { return (m_flags & e_activeFlag) == e_activeFlag; } inline bool b2Body::IsFixedRotation() const { return (m_flags & e_fixedRotationFlag) == e_fixedRotationFlag; } inline void b2Body::SetSleepingAllowed(bool flag) { if (flag) { m_flags |= e_autoSleepFlag; } else { m_flags &= ~e_autoSleepFlag; SetAwake(true); } } inline bool b2Body::IsSleepingAllowed() const { return (m_flags & e_autoSleepFlag) == e_autoSleepFlag; } inline b2Fixture* b2Body::GetFixtureList() { return m_fixtureList; } inline const b2Fixture* b2Body::GetFixtureList() const { return m_fixtureList; } inline b2JointEdge* b2Body::GetJointList() { return m_jointList; } inline const b2JointEdge* b2Body::GetJointList() const { return m_jointList; } inline b2ContactEdge* b2Body::GetContactList() { return m_contactList; } inline const b2ContactEdge* b2Body::GetContactList() const { return m_contactList; } inline b2Body* b2Body::GetNext() { return m_next; } inline const b2Body* b2Body::GetNext() const { return m_next; } inline void b2Body::SetUserData(void* data) { m_userData = data; } inline void* b2Body::GetUserData() const { return m_userData; } inline void b2Body::ApplyForce(const b2Vec2& force, const b2Vec2& point, bool wake) { if (m_type != b2_dynamicBody) { return; } if (wake && (m_flags & e_awakeFlag) == 0) { SetAwake(true); } // Don't accumulate a force if the body is sleeping. if (m_flags & e_awakeFlag) { m_force += force; m_torque += b2Cross(point - m_sweep.c, force); } } inline void b2Body::ApplyForceToCenter(const b2Vec2& force, bool wake) { if (m_type != b2_dynamicBody) { return; } if (wake && (m_flags & e_awakeFlag) == 0) { SetAwake(true); } // Don't accumulate a force if the body is sleeping if (m_flags & e_awakeFlag) { m_force += force; } } inline void b2Body::ApplyTorque(float32 torque, bool wake) { if (m_type != b2_dynamicBody) { return; } if (wake && (m_flags & e_awakeFlag) == 0) { SetAwake(true); } // Don't accumulate a force if the body is sleeping if (m_flags & e_awakeFlag) { m_torque += torque; } } inline void b2Body::ApplyLinearImpulse(const b2Vec2& impulse, const b2Vec2& point, bool wake) { if (m_type != b2_dynamicBody) { return; } if (wake && (m_flags & e_awakeFlag) == 0) { SetAwake(true); } // Don't accumulate velocity if the body is sleeping if (m_flags & e_awakeFlag) { m_linearVelocity += m_invMass * impulse; m_angularVelocity += m_invI * b2Cross(point - m_sweep.c, impulse); } } inline void b2Body::ApplyAngularImpulse(float32 impulse, bool wake) { if (m_type != b2_dynamicBody) { return; } if (wake && (m_flags & e_awakeFlag) == 0) { SetAwake(true); } // Don't accumulate velocity if the body is sleeping if (m_flags & e_awakeFlag) { m_angularVelocity += m_invI * impulse; } } inline void b2Body::SynchronizeTransform() { m_xf.q.Set(m_sweep.a); m_xf.p = m_sweep.c - b2Mul(m_xf.q, m_sweep.localCenter); } inline void b2Body::Advance(float32 alpha) { // Advance to the new safe time. This doesn't sync the broad-phase. m_sweep.Advance(alpha); m_sweep.c = m_sweep.c0; m_sweep.a = m_sweep.a0; m_xf.q.Set(m_sweep.a); m_xf.p = m_sweep.c - b2Mul(m_xf.q, m_sweep.localCenter); } inline b2World* b2Body::GetWorld() { return m_world; } inline const b2World* b2Body::GetWorld() const { return m_world; } #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/b2ContactManager.h ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_CONTACT_MANAGER_H #define B2_CONTACT_MANAGER_H #include "../Collision/b2BroadPhase.h" class b2Contact; class b2ContactFilter; class b2ContactListener; class b2BlockAllocator; // Delegate of b2World. class b2ContactManager { public: b2ContactManager(); // Broad-phase callback. void AddPair(void* proxyUserDataA, void* proxyUserDataB); void FindNewContacts(); void Destroy(b2Contact* c); void Collide(); b2BroadPhase m_broadPhase; b2Contact* m_contactList; int32 m_contactCount; b2ContactFilter* m_contactFilter; b2ContactListener* m_contactListener; b2BlockAllocator* m_allocator; }; #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/b2Fixture.h ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_FIXTURE_H #define B2_FIXTURE_H #include "b2Body.h" #include "../Collision/b2Collision.h" #include "../Collision/Shapes/b2Shape.h" class b2BlockAllocator; class b2Body; class b2BroadPhase; class b2Fixture; /// This holds contact filtering data. struct b2Filter { b2Filter() { categoryBits = 0x0001; maskBits = 0xFFFF; groupIndex = 0; } /// The collision category bits. Normally you would just set one bit. uint16 categoryBits; /// The collision mask bits. This states the categories that this /// shape would accept for collision. uint16 maskBits; /// Collision groups allow a certain group of objects to never collide (negative) /// or always collide (positive). Zero means no collision group. Non-zero group /// filtering always wins against the mask bits. int16 groupIndex; }; /// A fixture definition is used to create a fixture. This class defines an /// abstract fixture definition. You can reuse fixture definitions safely. struct b2FixtureDef { /// The constructor sets the default fixture definition values. b2FixtureDef() { shape = NULL; userData = NULL; friction = 0.2f; restitution = 0.0f; density = 0.0f; isSensor = false; } /// The shape, this must be set. The shape will be cloned, so you /// can create the shape on the stack. const b2Shape* shape; /// Use this to store application specific fixture data. void* userData; /// The friction coefficient, usually in the range [0,1]. float32 friction; /// The restitution (elasticity) usually in the range [0,1]. float32 restitution; /// The density, usually in kg/m^2. float32 density; /// A sensor shape collects contact information but never generates a collision /// response. bool isSensor; /// Contact filtering data. b2Filter filter; }; /// This proxy is used internally to connect fixtures to the broad-phase. struct b2FixtureProxy { b2AABB aabb; b2Fixture* fixture; int32 childIndex; int32 proxyId; }; /// A fixture is used to attach a shape to a body for collision detection. A fixture /// inherits its transform from its parent. Fixtures hold additional non-geometric data /// such as friction, collision filters, etc. /// Fixtures are created via b2Body::CreateFixture. /// @warning you cannot reuse fixtures. class b2Fixture { public: /// Get the type of the child shape. You can use this to down cast to the concrete shape. /// @return the shape type. b2Shape::Type GetType() const; /// Get the child shape. You can modify the child shape, however you should not change the /// number of vertices because this will crash some collision caching mechanisms. /// Manipulating the shape may lead to non-physical behavior. b2Shape* GetShape(); const b2Shape* GetShape() const; /// Set if this fixture is a sensor. void SetSensor(bool sensor); /// Is this fixture a sensor (non-solid)? /// @return the true if the shape is a sensor. bool IsSensor() const; /// Set the contact filtering data. This will not update contacts until the next time /// step when either parent body is active and awake. /// This automatically calls Refilter. void SetFilterData(const b2Filter& filter); /// Get the contact filtering data. const b2Filter& GetFilterData() const; /// Call this if you want to establish collision that was previously disabled by b2ContactFilter::ShouldCollide. void Refilter(); /// Get the parent body of this fixture. This is NULL if the fixture is not attached. /// @return the parent body. b2Body* GetBody(); const b2Body* GetBody() const; /// Get the next fixture in the parent body's fixture list. /// @return the next shape. b2Fixture* GetNext(); const b2Fixture* GetNext() const; /// Get the user data that was assigned in the fixture definition. Use this to /// store your application specific data. void* GetUserData() const; /// Set the user data. Use this to store your application specific data. void SetUserData(void* data); /// Test a point for containment in this fixture. /// @param p a point in world coordinates. bool TestPoint(const b2Vec2& p) const; /// Cast a ray against this shape. /// @param output the ray-cast results. /// @param input the ray-cast input parameters. bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input, int32 childIndex) const; /// Get the mass data for this fixture. The mass data is based on the density and /// the shape. The rotational inertia is about the shape's origin. This operation /// may be expensive. void GetMassData(b2MassData* massData) const; /// Set the density of this fixture. This will _not_ automatically adjust the mass /// of the body. You must call b2Body::ResetMassData to update the body's mass. void SetDensity(float32 density); /// Get the density of this fixture. float32 GetDensity() const; /// Get the coefficient of friction. float32 GetFriction() const; /// Set the coefficient of friction. This will _not_ change the friction of /// existing contacts. void SetFriction(float32 friction); /// Get the coefficient of restitution. float32 GetRestitution() const; /// Set the coefficient of restitution. This will _not_ change the restitution of /// existing contacts. void SetRestitution(float32 restitution); /// Get the fixture's AABB. This AABB may be enlarge and/or stale. /// If you need a more accurate AABB, compute it using the shape and /// the body transform. const b2AABB& GetAABB(int32 childIndex) const; /// Dump this fixture to the log file. void Dump(int32 bodyIndex); protected: friend class b2Body; friend class b2World; friend class b2Contact; friend class b2ContactManager; b2Fixture(); // We need separation create/destroy functions from the constructor/destructor because // the destructor cannot access the allocator (no destructor arguments allowed by C++). void Create(b2BlockAllocator* allocator, b2Body* body, const b2FixtureDef* def); void Destroy(b2BlockAllocator* allocator); // These support body activation/deactivation. void CreateProxies(b2BroadPhase* broadPhase, const b2Transform& xf); void DestroyProxies(b2BroadPhase* broadPhase); void Synchronize(b2BroadPhase* broadPhase, const b2Transform& xf1, const b2Transform& xf2); float32 m_density; b2Fixture* m_next; b2Body* m_body; b2Shape* m_shape; float32 m_friction; float32 m_restitution; b2FixtureProxy* m_proxies; int32 m_proxyCount; b2Filter m_filter; bool m_isSensor; void* m_userData; }; inline b2Shape::Type b2Fixture::GetType() const { return m_shape->GetType(); } inline b2Shape* b2Fixture::GetShape() { return m_shape; } inline const b2Shape* b2Fixture::GetShape() const { return m_shape; } inline bool b2Fixture::IsSensor() const { return m_isSensor; } inline const b2Filter& b2Fixture::GetFilterData() const { return m_filter; } inline void* b2Fixture::GetUserData() const { return m_userData; } inline void b2Fixture::SetUserData(void* data) { m_userData = data; } inline b2Body* b2Fixture::GetBody() { return m_body; } inline const b2Body* b2Fixture::GetBody() const { return m_body; } inline b2Fixture* b2Fixture::GetNext() { return m_next; } inline const b2Fixture* b2Fixture::GetNext() const { return m_next; } inline void b2Fixture::SetDensity(float32 density) { b2Assert(b2IsValid(density) && density >= 0.0f); m_density = density; } inline float32 b2Fixture::GetDensity() const { return m_density; } inline float32 b2Fixture::GetFriction() const { return m_friction; } inline void b2Fixture::SetFriction(float32 friction) { m_friction = friction; } inline float32 b2Fixture::GetRestitution() const { return m_restitution; } inline void b2Fixture::SetRestitution(float32 restitution) { m_restitution = restitution; } inline bool b2Fixture::TestPoint(const b2Vec2& p) const { return m_shape->TestPoint(m_body->GetTransform(), p); } inline bool b2Fixture::RayCast(b2RayCastOutput* output, const b2RayCastInput& input, int32 childIndex) const { return m_shape->RayCast(output, input, m_body->GetTransform(), childIndex); } inline void b2Fixture::GetMassData(b2MassData* massData) const { m_shape->ComputeMass(massData, m_density); } inline const b2AABB& b2Fixture::GetAABB(int32 childIndex) const { b2Assert(0 <= childIndex && childIndex < m_proxyCount); return m_proxies[childIndex].aabb; } #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/b2Island.h ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_ISLAND_H #define B2_ISLAND_H #include "../Common/b2Math.h" #include "b2Body.h" #include "b2TimeStep.h" class b2Contact; class b2Joint; class b2StackAllocator; class b2ContactListener; struct b2ContactVelocityConstraint; struct b2Profile; /// This is an internal class. class b2Island { public: b2Island(int32 bodyCapacity, int32 contactCapacity, int32 jointCapacity, b2StackAllocator* allocator, b2ContactListener* listener); ~b2Island(); void Clear() { m_bodyCount = 0; m_contactCount = 0; m_jointCount = 0; } void Solve(b2Profile* profile, const b2TimeStep& step, const b2Vec2& gravity, bool allowSleep); void SolveTOI(const b2TimeStep& subStep, int32 toiIndexA, int32 toiIndexB); void Add(b2Body* body) { b2Assert(m_bodyCount < m_bodyCapacity); body->m_islandIndex = m_bodyCount; m_bodies[m_bodyCount] = body; ++m_bodyCount; } void Add(b2Contact* contact) { b2Assert(m_contactCount < m_contactCapacity); m_contacts[m_contactCount++] = contact; } void Add(b2Joint* joint) { b2Assert(m_jointCount < m_jointCapacity); m_joints[m_jointCount++] = joint; } void Report(const b2ContactVelocityConstraint* constraints); b2StackAllocator* m_allocator; b2ContactListener* m_listener; b2Body** m_bodies; b2Contact** m_contacts; b2Joint** m_joints; b2Position* m_positions; b2Velocity* m_velocities; int32 m_bodyCount; int32 m_jointCount; int32 m_contactCount; int32 m_bodyCapacity; int32 m_contactCapacity; int32 m_jointCapacity; }; #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/b2TimeStep.h ================================================ /* * Copyright (c) 2006-2011 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_TIME_STEP_H #define B2_TIME_STEP_H #include "../Common/b2Math.h" /// Profiling data. Times are in milliseconds. struct b2Profile { float32 step; float32 collide; float32 solve; float32 solveInit; float32 solveVelocity; float32 solvePosition; float32 broadphase; float32 solveTOI; }; /// This is an internal structure. struct b2TimeStep { float32 dt; // time step float32 inv_dt; // inverse time step (0 if dt == 0). float32 dtRatio; // dt * inv_dt0 int32 velocityIterations; int32 positionIterations; bool warmStarting; }; /// This is an internal structure. struct b2Position { b2Vec2 c; float32 a; }; /// This is an internal structure. struct b2Velocity { b2Vec2 v; float32 w; }; /// Solver Data struct b2SolverData { b2TimeStep step; b2Position* positions; b2Velocity* velocities; }; #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/b2World.h ================================================ /* * Copyright (c) 2006-2011 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_WORLD_H #define B2_WORLD_H #include "../Common/b2Math.h" #include "../Common/b2BlockAllocator.h" #include "../Common/b2StackAllocator.h" #include "b2ContactManager.h" #include "b2WorldCallbacks.h" #include "b2TimeStep.h" struct b2AABB; struct b2BodyDef; struct b2Color; struct b2JointDef; class b2Body; class b2Draw; class b2Fixture; class b2Joint; /// The world class manages all physics entities, dynamic simulation, /// and asynchronous queries. The world also contains efficient memory /// management facilities. class b2World { public: /// Construct a world object. /// @param gravity the world gravity vector. b2World(const b2Vec2& gravity); /// Destruct the world. All physics entities are destroyed and all heap memory is released. ~b2World(); /// Register a destruction listener. The listener is owned by you and must /// remain in scope. void SetDestructionListener(b2DestructionListener* listener); /// Register a contact filter to provide specific control over collision. /// Otherwise the default filter is used (b2_defaultFilter). The listener is /// owned by you and must remain in scope. void SetContactFilter(b2ContactFilter* filter); /// Register a contact event listener. The listener is owned by you and must /// remain in scope. void SetContactListener(b2ContactListener* listener); /// Register a routine for debug drawing. The debug draw functions are called /// inside with b2World::DrawDebugData method. The debug draw object is owned /// by you and must remain in scope. void SetDebugDraw(b2Draw* debugDraw); /// Create a rigid body given a definition. No reference to the definition /// is retained. /// @warning This function is locked during callbacks. b2Body* CreateBody(const b2BodyDef* def); /// Destroy a rigid body given a definition. No reference to the definition /// is retained. This function is locked during callbacks. /// @warning This automatically deletes all associated shapes and joints. /// @warning This function is locked during callbacks. void DestroyBody(b2Body* body); /// Create a joint to constrain bodies together. No reference to the definition /// is retained. This may cause the connected bodies to cease colliding. /// @warning This function is locked during callbacks. b2Joint* CreateJoint(const b2JointDef* def); /// Destroy a joint. This may cause the connected bodies to begin colliding. /// @warning This function is locked during callbacks. void DestroyJoint(b2Joint* joint); /// Take a time step. This performs collision detection, integration, /// and constraint solution. /// @param timeStep the amount of time to simulate, this should not vary. /// @param velocityIterations for the velocity constraint solver. /// @param positionIterations for the position constraint solver. void Step( float32 timeStep, int32 velocityIterations, int32 positionIterations); /// Manually clear the force buffer on all bodies. By default, forces are cleared automatically /// after each call to Step. The default behavior is modified by calling SetAutoClearForces. /// The purpose of this function is to support sub-stepping. Sub-stepping is often used to maintain /// a fixed sized time step under a variable frame-rate. /// When you perform sub-stepping you will disable auto clearing of forces and instead call /// ClearForces after all sub-steps are complete in one pass of your game loop. /// @see SetAutoClearForces void ClearForces(); /// Call this to draw shapes and other debug draw data. This is intentionally non-const. void DrawDebugData(); /// Query the world for all fixtures that potentially overlap the /// provided AABB. /// @param callback a user implemented callback class. /// @param aabb the query box. void QueryAABB(b2QueryCallback* callback, const b2AABB& aabb) const; /// Ray-cast the world for all fixtures in the path of the ray. Your callback /// controls whether you get the closest point, any point, or n-points. /// The ray-cast ignores shapes that contain the starting point. /// @param callback a user implemented callback class. /// @param point1 the ray starting point /// @param point2 the ray ending point void RayCast(b2RayCastCallback* callback, const b2Vec2& point1, const b2Vec2& point2) const; /// Get the world body list. With the returned body, use b2Body::GetNext to get /// the next body in the world list. A NULL body indicates the end of the list. /// @return the head of the world body list. b2Body* GetBodyList(); const b2Body* GetBodyList() const; /// Get the world joint list. With the returned joint, use b2Joint::GetNext to get /// the next joint in the world list. A NULL joint indicates the end of the list. /// @return the head of the world joint list. b2Joint* GetJointList(); const b2Joint* GetJointList() const; /// Get the world contact list. With the returned contact, use b2Contact::GetNext to get /// the next contact in the world list. A NULL contact indicates the end of the list. /// @return the head of the world contact list. /// @warning contacts are created and destroyed in the middle of a time step. /// Use b2ContactListener to avoid missing contacts. b2Contact* GetContactList(); const b2Contact* GetContactList() const; /// Enable/disable sleep. void SetAllowSleeping(bool flag); bool GetAllowSleeping() const { return m_allowSleep; } /// Enable/disable warm starting. For testing. void SetWarmStarting(bool flag) { m_warmStarting = flag; } bool GetWarmStarting() const { return m_warmStarting; } /// Enable/disable continuous physics. For testing. void SetContinuousPhysics(bool flag) { m_continuousPhysics = flag; } bool GetContinuousPhysics() const { return m_continuousPhysics; } /// Enable/disable single stepped continuous physics. For testing. void SetSubStepping(bool flag) { m_subStepping = flag; } bool GetSubStepping() const { return m_subStepping; } /// Get the number of broad-phase proxies. int32 GetProxyCount() const; /// Get the number of bodies. int32 GetBodyCount() const; /// Get the number of joints. int32 GetJointCount() const; /// Get the number of contacts (each may have 0 or more contact points). int32 GetContactCount() const; /// Get the height of the dynamic tree. int32 GetTreeHeight() const; /// Get the balance of the dynamic tree. int32 GetTreeBalance() const; /// Get the quality metric of the dynamic tree. The smaller the better. /// The minimum is 1. float32 GetTreeQuality() const; /// Change the global gravity vector. void SetGravity(const b2Vec2& gravity); /// Get the global gravity vector. b2Vec2 GetGravity() const; /// Is the world locked (in the middle of a time step). bool IsLocked() const; /// Set flag to control automatic clearing of forces after each time step. void SetAutoClearForces(bool flag); /// Get the flag that controls automatic clearing of forces after each time step. bool GetAutoClearForces() const; /// Shift the world origin. Useful for large worlds. /// The body shift formula is: position -= newOrigin /// @param newOrigin the new origin with respect to the old origin void ShiftOrigin(const b2Vec2& newOrigin); /// Get the contact manager for testing. const b2ContactManager& GetContactManager() const; /// Get the current profile. const b2Profile& GetProfile() const; /// Dump the world into the log file. /// @warning this should be called outside of a time step. void Dump(); private: // m_flags enum { e_newFixture = 0x0001, e_locked = 0x0002, e_clearForces = 0x0004 }; friend class b2Body; friend class b2Fixture; friend class b2ContactManager; friend class b2Controller; void Solve(const b2TimeStep& step); void SolveTOI(const b2TimeStep& step); void DrawJoint(b2Joint* joint); void DrawShape(b2Fixture* shape, const b2Transform& xf, const b2Color& color); b2BlockAllocator m_blockAllocator; b2StackAllocator m_stackAllocator; int32 m_flags; b2ContactManager m_contactManager; b2Body* m_bodyList; b2Joint* m_jointList; int32 m_bodyCount; int32 m_jointCount; b2Vec2 m_gravity; bool m_allowSleep; b2DestructionListener* m_destructionListener; b2Draw* m_debugDraw; // This is used to compute the time step ratio to // support a variable time step. float32 m_inv_dt0; // These are for debugging the solver. bool m_warmStarting; bool m_continuousPhysics; bool m_subStepping; bool m_stepComplete; b2Profile m_profile; }; inline b2Body* b2World::GetBodyList() { return m_bodyList; } inline const b2Body* b2World::GetBodyList() const { return m_bodyList; } inline b2Joint* b2World::GetJointList() { return m_jointList; } inline const b2Joint* b2World::GetJointList() const { return m_jointList; } inline b2Contact* b2World::GetContactList() { return m_contactManager.m_contactList; } inline const b2Contact* b2World::GetContactList() const { return m_contactManager.m_contactList; } inline int32 b2World::GetBodyCount() const { return m_bodyCount; } inline int32 b2World::GetJointCount() const { return m_jointCount; } inline int32 b2World::GetContactCount() const { return m_contactManager.m_contactCount; } inline void b2World::SetGravity(const b2Vec2& gravity) { m_gravity = gravity; } inline b2Vec2 b2World::GetGravity() const { return m_gravity; } inline bool b2World::IsLocked() const { return (m_flags & e_locked) == e_locked; } inline void b2World::SetAutoClearForces(bool flag) { if (flag) { m_flags |= e_clearForces; } else { m_flags &= ~e_clearForces; } } /// Get the flag that controls automatic clearing of forces after each time step. inline bool b2World::GetAutoClearForces() const { return (m_flags & e_clearForces) == e_clearForces; } inline const b2ContactManager& b2World::GetContactManager() const { return m_contactManager; } inline const b2Profile& b2World::GetProfile() const { return m_profile; } #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Dynamics/b2WorldCallbacks.h ================================================ /* * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_WORLD_CALLBACKS_H #define B2_WORLD_CALLBACKS_H #include "../Common/b2Settings.h" struct b2Vec2; struct b2Transform; class b2Fixture; class b2Body; class b2Joint; class b2Contact; struct b2ContactResult; struct b2Manifold; /// Joints and fixtures are destroyed when their associated /// body is destroyed. Implement this listener so that you /// may nullify references to these joints and shapes. class b2DestructionListener { public: virtual ~b2DestructionListener() {} /// Called when any joint is about to be destroyed due /// to the destruction of one of its attached bodies. virtual void SayGoodbye(b2Joint* joint) = 0; /// Called when any fixture is about to be destroyed due /// to the destruction of its parent body. virtual void SayGoodbye(b2Fixture* fixture) = 0; }; /// Implement this class to provide collision filtering. In other words, you can implement /// this class if you want finer control over contact creation. class b2ContactFilter { public: virtual ~b2ContactFilter() {} /// Return true if contact calculations should be performed between these two shapes. /// @warning for performance reasons this is only called when the AABBs begin to overlap. virtual bool ShouldCollide(b2Fixture* fixtureA, b2Fixture* fixtureB); }; /// Contact impulses for reporting. Impulses are used instead of forces because /// sub-step forces may approach infinity for rigid body collisions. These /// match up one-to-one with the contact points in b2Manifold. struct b2ContactImpulse { float32 normalImpulses[b2_maxManifoldPoints]; float32 tangentImpulses[b2_maxManifoldPoints]; int32 count; }; /// Implement this class to get contact information. You can use these results for /// things like sounds and game logic. You can also get contact results by /// traversing the contact lists after the time step. However, you might miss /// some contacts because continuous physics leads to sub-stepping. /// Additionally you may receive multiple callbacks for the same contact in a /// single time step. /// You should strive to make your callbacks efficient because there may be /// many callbacks per time step. /// @warning You cannot create/destroy Box2D entities inside these callbacks. class b2ContactListener { public: virtual ~b2ContactListener() {} /// Called when two fixtures begin to touch. virtual void BeginContact(b2Contact* contact) { B2_NOT_USED(contact); } /// Called when two fixtures cease to touch. virtual void EndContact(b2Contact* contact) { B2_NOT_USED(contact); } /// This is called after a contact is updated. This allows you to inspect a /// contact before it goes to the solver. If you are careful, you can modify the /// contact manifold (e.g. disable contact). /// A copy of the old manifold is provided so that you can detect changes. /// Note: this is called only for awake bodies. /// Note: this is called even when the number of contact points is zero. /// Note: this is not called for sensors. /// Note: if you set the number of contact points to zero, you will not /// get an EndContact callback. However, you may get a BeginContact callback /// the next step. virtual void PreSolve(b2Contact* contact, const b2Manifold* oldManifold) { B2_NOT_USED(contact); B2_NOT_USED(oldManifold); } /// This lets you inspect a contact after the solver is finished. This is useful /// for inspecting impulses. /// Note: the contact manifold does not include time of impact impulses, which can be /// arbitrarily large if the sub-step is small. Hence the impulse is provided explicitly /// in a separate data structure. /// Note: this is only called for contacts that are touching, solid, and awake. virtual void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse) { B2_NOT_USED(contact); B2_NOT_USED(impulse); } }; /// Callback class for AABB queries. /// See b2World::Query class b2QueryCallback { public: virtual ~b2QueryCallback() {} /// Called for each fixture found in the query AABB. /// @return false to terminate the query. virtual bool ReportFixture(b2Fixture* fixture) = 0; }; /// Callback class for ray casts. /// See b2World::RayCast class b2RayCastCallback { public: virtual ~b2RayCastCallback() {} /// Called for each fixture found in the query. You control how the ray cast /// proceeds by returning a float: /// return -1: ignore this fixture and continue /// return 0: terminate the ray cast /// return fraction: clip the ray to this point /// return 1: don't clip the ray and continue /// @param fixture the fixture hit by the ray /// @param point the point of initial intersection /// @param normal the normal vector at the point of intersection /// @return -1 to filter, 0 to terminate, fraction to clip the ray for /// closest hit, 1 to continue virtual float32 ReportFixture( b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float32 fraction) = 0; }; #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/Box2D/Rope/b2Rope.h ================================================ /* * Copyright (c) 2011 Erin Catto http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #ifndef B2_ROPE_H #define B2_ROPE_H #include "../Common/b2Math.h" class b2Draw; /// struct b2RopeDef { b2RopeDef() { vertices = NULL; count = 0; masses = NULL; gravity.SetZero(); damping = 0.1f; k2 = 0.9f; k3 = 0.1f; } /// b2Vec2* vertices; /// int32 count; /// float32* masses; /// b2Vec2 gravity; /// float32 damping; /// Stretching stiffness float32 k2; /// Bending stiffness. Values above 0.5 can make the simulation blow up. float32 k3; }; /// class b2Rope { public: b2Rope(); ~b2Rope(); /// void Initialize(const b2RopeDef* def); /// void Step(float32 timeStep, int32 iterations); /// int32 GetVertexCount() const { return m_count; } /// const b2Vec2* GetVertices() const { return m_ps; } /// void Draw(b2Draw* draw) const; /// void SetAngle(float32 angle); private: void SolveC2(); void SolveC3(); int32 m_count; b2Vec2* m_ps; b2Vec2* m_p0s; b2Vec2* m_vs; float32* m_ims; float32* m_Ls; float32* m_as; b2Vec2 m_gravity; float32 m_damping; float32 m_k2; float32 m_k3; }; #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/Config.hpp ================================================ /********************************************************************* Matt Marchant 2016 - 2020 http://trederia.blogspot.com tmxlite - Zlib license. 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. *********************************************************************/ #pragma once //check which platform we're on and create export macros as necessary #if !defined(TMXLITE_STATIC) #if defined(_WIN32) //windows compilers need specific (and different) keywords for export #define TMXLITE_EXPORT_API __declspec(dllexport) //for vc compilers we also need to turn off this annoying C4251 warning #ifdef _MSC_VER #pragma warning(disable: 4251) #endif //_MSC_VER #else //linux, FreeBSD, Mac OS X #if __GNUC__ >= 4 //gcc 4 has special keywords for showing/hiding symbols, //the same keyword is used for both importing and exporting #define TMXLITE_EXPORT_API __attribute__ ((__visibility__ ("default"))) #else //gcc < 4 has no mechanism to explicitly hide symbols, everything's exported #define TMXLITE_EXPORT_API #endif //__GNUC__ #endif //_WIN32 #else //static build doesn't need import/export macros #define TMXLITE_EXPORT_API #endif //TMXLITE_STATIC ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/FreeFuncs.cpp ================================================ /********************************************************************* Matt Marchant 2016 http://trederia.blogspot.com tmxlite - Zlib license. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. *********************************************************************/ #include "FreeFuncs.hpp" #include "detail/Log.hpp" #include "miniz.h" #include bool tmx::decompress(const char* source, std::vector& dest, std::size_t inSize, std::size_t expectedSize) { if (!source) { LOG("Input string is empty, decompression failed.", Logger::Type::Error); return false; } int currentSize = static_cast(expectedSize); std::vector byteArray(expectedSize / sizeof(unsigned char)); z_stream stream; stream.zalloc = Z_NULL; stream.zfree = Z_NULL; stream.opaque = Z_NULL; stream.next_in = (Bytef*)source; stream.avail_in = static_cast(inSize); stream.next_out = (Bytef*)byteArray.data(); stream.avail_out = static_cast(expectedSize); //we'd prefer to use inflateInit2 but it appears //to be incorrect in miniz. This is fine for zlib //compressed data, but gzip compressed streams //will fail to inflate. IMO still preferable to //trying to build/link zlib if (inflateInit(&stream/*, 15 + 32*/) != Z_OK) { LOG("inflate init failed", Logger::Type::Error); return false; } int result = 0; do { result = inflate(&stream, Z_SYNC_FLUSH); switch (result) { case Z_NEED_DICT: case Z_STREAM_ERROR: result = Z_DATA_ERROR; case Z_DATA_ERROR: Logger::log("If using gzip compression try using zlib instead", Logger::Type::Info); case Z_MEM_ERROR: inflateEnd(&stream); Logger::log(std::to_string(result), Logger::Type::Error); return false; } if (result != Z_STREAM_END) { int oldSize = currentSize; currentSize *= 2; std::vector newArray(currentSize / sizeof(unsigned char)); std::memcpy(newArray.data(), byteArray.data(), currentSize / 2); byteArray = std::move(newArray); stream.next_out = (Bytef*)(byteArray.data() + oldSize); stream.avail_out = oldSize; } } while (result != Z_STREAM_END); if (stream.avail_in != 0) { LOG("stream.avail_in is 0", Logger::Type::Error); LOG("zlib decompression failed.", Logger::Type::Error); return false; } const int outSize = currentSize - stream.avail_out; inflateEnd(&stream); std::vector newArray(outSize / sizeof(unsigned char)); std::memcpy(newArray.data(), byteArray.data(), outSize); byteArray = std::move(newArray); //copy bytes to vector dest.insert(dest.begin(), byteArray.begin(), byteArray.end()); return true; } ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/FreeFuncs.hpp ================================================ /********************************************************************* Matt Marchant 2016 http://trederia.blogspot.com tmxlite - Zlib license. 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. *********************************************************************/ /********************************************************************* base64_decode Copyright (C) 2004-2008 René Nyffenegger This source code is provided 'as-is', without any express or implied warranty. In no event will the author 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 source code must not be misrepresented; you must not claim that you wrote the original source code. If you use this source code 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 source code. 3. This notice may not be removed or altered from any source distribution. René Nyffenegger rene.nyffenegger@adp-gmbh.ch *********************************************************************/ #pragma once #include "detail/Android.hpp" #include "detail/Log.hpp" #include "Types.hpp" #include #include #include #include #include namespace tmx { //using inline here just to supress unused warnings on gcc bool decompress(const char* source, std::vector& dest, std::size_t inSize, std::size_t expectedSize); static inline std::string base64_decode(std::string const& encoded_string) { static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; std::function is_base64 = [](unsigned char c)->bool { return (isalnum(c) || (c == '+') || (c == '/')); }; auto in_len = encoded_string.size(); int i = 0; int j = 0; int in_ = 0; unsigned char char_array_4[4], char_array_3[3]; std::string ret; while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { char_array_4[i++] = encoded_string[in_]; in_++; if (i == 4) { for (i = 0; i < 4; i++) { char_array_4[i] = static_cast(base64_chars.find(char_array_4[i])); } char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; for (i = 0; (i < 3); i++) { ret += char_array_3[i]; } i = 0; } } if (i) { for (j = i; j < 4; j++) { char_array_4[j] = 0; } for (j = 0; j < 4; j++) { char_array_4[j] = static_cast(base64_chars.find(char_array_4[j])); } char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; for (j = 0; (j < i - 1); j++) { ret += char_array_3[j]; } } return ret; } static inline Colour colourFromString(std::string str) { //removes preceding # auto result = str.find_last_of('#'); if (result != std::string::npos) { str = str.substr(result + 1); } if (str.size() == 6 || str.size() == 8) { unsigned int value, r, g, b; unsigned int a = 255; std::stringstream input(str); input >> std::hex >> value; r = (value >> 16) & 0xff; g = (value >> 8) & 0xff; b = value & 0xff; if (str.size() == 8) { a = (value >> 24) & 0xff; } return{ std::uint8_t(r), std::uint8_t(g), std::uint8_t(b), std::uint8_t(a) }; } Logger::log(str + ": not a valid colour string", Logger::Type::Error); return{}; } static inline std::string resolveFilePath(std::string path, const std::string& workingDir) { static const std::string match("../"); std::size_t result = path.find(match); std::size_t count = 0; while (result != std::string::npos) { count++; path = path.substr(result + match.size()); result = path.find(match); } if (workingDir.empty()) return path; std::string outPath = workingDir; for (auto i = 0u; i < count; ++i) { result = outPath.find_last_of('/'); if (result != std::string::npos) { outPath = outPath.substr(0, result); } } // this does only work on windows #ifndef __ANDROID__ return std::move(outPath += '/' + path); #endif // todo: make resolveFilePath work with subfolders on // android - currently only the root folder is working #ifdef __ANDROID__ return std::move(outPath = path); #endif } } ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/ImageLayer.cpp ================================================ /********************************************************************* Matt Marchant 2016 http://trederia.blogspot.com tmxlite - Zlib license. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. *********************************************************************/ #include "ImageLayer.hpp" #include "FreeFuncs.hpp" #include "detail/pugixml.hpp" #include "detail/Log.hpp" using namespace tmx; ImageLayer::ImageLayer(const std::string& workingDir) : m_workingDir (workingDir), m_hasTransparency (false) { } //public void ImageLayer::parse(const pugi::xml_node& node, Map*) { std::string attribName = node.name(); if (attribName != "imagelayer") { Logger::log("Node not an image layer, node skipped", Logger::Type::Error); return; } setName(node.attribute("name").as_string()); setOpacity(node.attribute("opacity").as_float(1.f)); setVisible(node.attribute("visible").as_bool(true)); setOffset(node.attribute("offsetx").as_int(), node.attribute("offsety").as_int()); setSize(node.attribute("width").as_uint(), node.attribute("height").as_uint()); for (const auto& child : node.children()) { attribName = child.name(); if (attribName == "image") { attribName = child.attribute("source").as_string(); if (attribName.empty()) { Logger::log("Image Layer has missing source property", Logger::Type::Warning); return; } if (child.attribute("width") && child.attribute("height")) { m_imageSize.x = child.attribute("width").as_uint(); m_imageSize.y = child.attribute("height").as_uint(); } m_filePath = resolveFilePath(attribName, m_workingDir); if (child.attribute("trans")) { attribName = child.attribute("trans").as_string(); m_transparencyColour = colourFromString(attribName); m_hasTransparency = true; } } else if (attribName == "properties") { for (const auto& p : child.children()) { addProperty(p); } } } } ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/ImageLayer.hpp ================================================ /********************************************************************* Matt Marchant 2016 - 2019 http://trederia.blogspot.com tmxlite - Zlib license. 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. *********************************************************************/ #pragma once #include "Config.hpp" #include "Layer.hpp" #include "Types.hpp" namespace tmx { /*! \brief Image layers contain a single image which make up that layer. The parser contains the fully resolved path to the image relative to the working directory. */ class TMXLITE_EXPORT_API ImageLayer final : public Layer { public: explicit ImageLayer(const std::string&); ~ImageLayer() = default; Type getType() const override { return Layer::Type::Image; } void parse(const pugi::xml_node&, Map*) override; /*! \brief Returns the path, relative to the working directory, of the image used by the image layer. */ const std::string& getImagePath() const { return m_filePath; } /*! \brief Returns the colour used by the image to represent transparent pixels. By default this is (0, 0, 0, 0) */ const Colour& getTransparencyColour() const { return m_transparencyColour; } /*! \brief Returns true if the image used by this layer specifically states a colour to use as transparency */ bool hasTransparency() const { return m_hasTransparency; } /*! \brief Returns the size of the image of the image layer in pixels. */ const Vector2u& getImageSize() const { return m_imageSize; } private: std::string m_workingDir; std::string m_filePath; Colour m_transparencyColour; bool m_hasTransparency; Vector2u m_imageSize; }; template <> inline ImageLayer& Layer::getLayerAs() { assert(getType() == Type::Image); return *dynamic_cast(this); } } ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/Layer.hpp ================================================ /********************************************************************* Matt Marchant 2016 - 2019 http://trederia.blogspot.com tmxlite - Zlib license. 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. *********************************************************************/ #pragma once #include "Config.hpp" #include "Property.hpp" #include "Types.hpp" #include #include #include namespace pugi { class xml_node; } namespace tmx { class Map; class TileLayer; class ObjectGroup; class ImageLayer; class LayerGroup; /*! \brief Represents a layer of a tmx format tile map. This is an abstract base class from which all layer types are derived. */ class TMXLITE_EXPORT_API Layer { public: using Ptr = std::unique_ptr; Layer() : m_opacity(1.f), m_visible(true) {}; virtual ~Layer() = default; /*! \brief Layer type as returned by getType() Tile: this layer is a TileLayer type Object: This layer is an ObjectGroup type Image: This layer is an ImageLayer type Group: This layer is a LayerGroup type */ enum class Type { Tile, Object, Image, Group }; /*! \brief Returns a Type value representing the concrete type. Use this when deciding which conrete layer type to use when calling the templated function getLayerAs() */ virtual Type getType() const = 0; /*! \brief Use this to get a reference to the concrete layer type which this layer points to. Use getType() to return the type value of this layer and determine if the concrete type is TileLayer, ObjectGroup, ImageLayer, or LayerGroup */ template T& getLayerAs(); /*{ throw("Not a valid layer type"); return *dynamic_cast(this); }*/ template const T& getLayerAs() const { return getLayerAs(); } /*! \brief Attempts to parse the specific node layer type */ virtual void parse(const pugi::xml_node&, Map* = nullptr) = 0; /*! \brief Returns the name of the layer */ const std::string& getName() const { return m_name; } /*! \brief Returns the opacity value for the layer */ float getOpacity() const { return m_opacity; } /*! \brief Returns whether this layer is visible or not */ bool getVisible() const { return m_visible; } /*! \brief Returns the offset from the top left corner of the layer, in pixels */ const Vector2i& getOffset() const { return m_offset; } /*! \brief Returns the size of the layer, in pixels. This will be the same as the map size for fixed size maps. */ const Vector2u& getSize() const { return m_size; } /*! \brief Returns the list of properties of this layer */ const std::vector& getProperties() const { return m_properties; } protected: void setName(const std::string& name) { m_name = name; } void setOpacity(float opacity) { m_opacity = opacity; } void setVisible(bool visible) { m_visible = visible; } void setOffset(std::int32_t x, std::int32_t y) { m_offset = Vector2i(x, y); } void setSize(std::uint32_t width, std::uint32_t height) { m_size = Vector2u(width, height); } void addProperty(const pugi::xml_node& node) { m_properties.emplace_back(); m_properties.back().parse(node); } private: std::string m_name; float m_opacity; bool m_visible; Vector2i m_offset; Vector2u m_size; std::vector m_properties; }; } ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/LayerGroup.cpp ================================================ /********************************************************************* Grant Gangi 2019 tmxlite - Zlib license. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. *********************************************************************/ #include "detail/pugixml.hpp" #include "LayerGroup.hpp" #include "FreeFuncs.hpp" #include "ObjectGroup.hpp" #include "ImageLayer.hpp" #include "TileLayer.hpp" #include "detail/Log.hpp" using namespace tmx; LayerGroup::LayerGroup(const std::string& workingDir, const Vector2u& tileCount) : m_workingDir(workingDir), m_tileCount(tileCount) { } //public void LayerGroup::parse(const pugi::xml_node& node, Map* map) { assert(map); std::string attribString = node.name(); if (attribString != "group") { Logger::log("Node was not a group layer, node will be skipped.", Logger::Type::Error); return; } setName(node.attribute("name").as_string()); setOpacity(node.attribute("opacity").as_float(1.f)); setVisible(node.attribute("visible").as_bool(true)); setOffset(node.attribute("offsetx").as_int(), node.attribute("offsety").as_int()); setSize(node.attribute("width").as_uint(), node.attribute("height").as_uint()); // parse children for (const auto& child : node.children()) { attribString = child.name(); if (attribString == "properties") { for (const auto& p : child.children()) { addProperty(p); } } else if (attribString == "layer") { m_layers.emplace_back(std::make_unique(m_tileCount.x * m_tileCount.y)); m_layers.back()->parse(child, map); } else if (attribString == "objectgroup") { m_layers.emplace_back(std::make_unique()); m_layers.back()->parse(child, map); } else if (attribString == "imagelayer") { m_layers.emplace_back(std::make_unique(m_workingDir)); m_layers.back()->parse(child, map); } else if (attribString == "group") { m_layers.emplace_back(std::make_unique(m_workingDir, m_tileCount)); m_layers.back()->parse(child, map); } else { LOG("Unidentified name " + attribString + ": node skipped", Logger::Type::Warning); } } } ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/LayerGroup.hpp ================================================ /********************************************************************* Grant Gangi 2019 tmxlite - Zlib license. 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. *********************************************************************/ #pragma once #include "Config.hpp" #include "Layer.hpp" #include "Types.hpp" #include namespace tmx { /*! \brief Layer groups are used to organize the layers of the map in a hierarchy. They can contain all other layer types including more layer groups to further nest layers. */ class TMXLITE_EXPORT_API LayerGroup final : public Layer { public: LayerGroup(const std::string& workDir, const Vector2u& tileCount); ~LayerGroup() = default; LayerGroup(const LayerGroup&) = delete; const LayerGroup& operator = (const LayerGroup&) = delete; LayerGroup(LayerGroup&&) = default; LayerGroup& operator = (LayerGroup&&) = default; Type getType() const override { return Layer::Type::Group; } void parse(const pugi::xml_node&, Map*) override; /*! \brief Returns a reference to the vector containing the layer data. Layers are pointer-to-baseclass, the concrete type of which can be found via Layer::getType() \see Layer */ const std::vector& getLayers() const { return m_layers; } private: std::vector m_layers; std::string m_workingDir; Vector2u m_tileCount; }; template <> inline LayerGroup& Layer::getLayerAs() { assert(getType() == Type::Group); return *dynamic_cast(this); } } ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/Map.cpp ================================================ /********************************************************************* Matt Marchant 2016 http://trederia.blogspot.com tmxlite - Zlib license. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. *********************************************************************/ #include "detail/pugixml.hpp" #include "Map.hpp" #include "FreeFuncs.hpp" #include "ObjectGroup.hpp" #include "ImageLayer.hpp" #include "TileLayer.hpp" #include "LayerGroup.hpp" #include "detail/Log.hpp" #include "detail/Android.hpp" #include using namespace tmx; namespace { std::string getFilePath(const std::string& path) { //TODO this doesn't actually check that there is a file at the //end of the path, or that it's even a valid path... static auto searchFunc = [](const char separator, const std::string& path)->std::string { std::size_t i = path.rfind(separator, path.length()); if (i != std::string::npos) { return(path.substr(0, i + 1)); } return ""; }; #ifdef _WIN32 //try windows formatted paths first std::string retVal = searchFunc('\\', path); if (!retVal.empty()) return retVal; #endif return searchFunc('/', path); } } Map::Map() : m_orientation (Orientation::None), m_renderOrder (RenderOrder::None), m_infinite (false), m_hexSideLength (0.f), m_staggerAxis (StaggerAxis::None), m_staggerIndex (StaggerIndex::None) { } //public bool Map::load(const std::string& path) { reset(); //open the doc pugi::xml_document doc; auto result = doc.load_file(path.c_str()); if (!result) { Logger::log("Failed opening " + path, Logger::Type::Error); Logger::log("Reason: " + std::string(result.description()), Logger::Type::Error); return false; } //make sure we have consistent path separators m_workingDirectory = path; std::replace(m_workingDirectory.begin(), m_workingDirectory.end(), '\\', '/'); m_workingDirectory = getFilePath(m_workingDirectory); if (!m_workingDirectory.empty() && m_workingDirectory.back() == '/') { m_workingDirectory.pop_back(); } //find the map node and bail if it doesn't exist auto mapNode = doc.child("map"); if (!mapNode) { Logger::log("Failed opening map: " + path + ", no map node found", Logger::Type::Error); return reset(); } return parseMapNode(mapNode); } bool Map::loadFromString(const std::string& data, const std::string& workingDir) { reset(); //open the doc pugi::xml_document doc; auto result = doc.load_string(data.c_str()); if (!result) { Logger::log("Failed opening map", Logger::Type::Error); Logger::log("Reason: " + std::string(result.description()), Logger::Type::Error); return false; } //make sure we have consistent path separators m_workingDirectory = workingDir; std::replace(m_workingDirectory.begin(), m_workingDirectory.end(), '\\', '/'); m_workingDirectory = getFilePath(m_workingDirectory); if (!m_workingDirectory.empty() && m_workingDirectory.back() == '/') { m_workingDirectory.pop_back(); } //find the map node and bail if it doesn't exist auto mapNode = doc.child("map"); if (!mapNode) { Logger::log("Failed opening map: no map node found", Logger::Type::Error); return reset(); } return parseMapNode(mapNode); } //private bool Map::parseMapNode(const pugi::xml_node& mapNode) { //parse map attributes std::size_t pointPos = 0; std::string attribString = mapNode.attribute("version").as_string(); if (attribString.empty() || (pointPos = attribString.find('.')) == std::string::npos) { Logger::log("Invalid map version value, map not loaded.", Logger::Type::Error); return reset(); } m_version.upper = STOI(attribString.substr(0, pointPos)); m_version.lower = STOI(attribString.substr(pointPos + 1)); attribString = mapNode.attribute("orientation").as_string(); if (attribString.empty()) { Logger::log("Missing map orientation attribute, map not loaded.", Logger::Type::Error); return reset(); } if (attribString == "orthogonal") { m_orientation = Orientation::Orthogonal; } else if (attribString == "isometric") { m_orientation = Orientation::Isometric; } else if (attribString == "staggered") { m_orientation = Orientation::Staggered; } else if (attribString == "hexagonal") { m_orientation = Orientation::Hexagonal; } else { Logger::log(attribString + " format maps aren't supported yet, sorry! Map not loaded", Logger::Type::Error); return reset(); } attribString = mapNode.attribute("renderorder").as_string(); //this property is optional for older version of map files if (!attribString.empty()) { if (attribString == "right-down") { m_renderOrder = RenderOrder::RightDown; } else if (attribString == "right-up") { m_renderOrder = RenderOrder::RightUp; } else if (attribString == "left-down") { m_renderOrder = RenderOrder::LeftDown; } else if (attribString == "left-up") { m_renderOrder = RenderOrder::LeftUp; } else { Logger::log(attribString + ": invalid render order. Map not loaded.", Logger::Type::Error); return reset(); } } if (mapNode.attribute("infinite")) { m_infinite = mapNode.attribute("infinite").as_int() != 0; } unsigned width = mapNode.attribute("width").as_int(); unsigned height = mapNode.attribute("height").as_int(); if (width && height) { m_tileCount = { width, height }; } else { Logger::log("Invalid map tile count, map not loaded", Logger::Type::Error); return reset(); } width = mapNode.attribute("tilewidth").as_int(); height = mapNode.attribute("tileheight").as_int(); if (width && height) { m_tileSize = { width, height }; } else { Logger::log("Invalid tile size, map not loaded", Logger::Type::Error); return reset(); } m_hexSideLength = mapNode.attribute("hexsidelength").as_float(); if (m_orientation == Orientation::Hexagonal && m_hexSideLength <= 0) { Logger::log("Invalid he side length found, map not loaded", Logger::Type::Error); return reset(); } attribString = mapNode.attribute("staggeraxis").as_string(); if (attribString == "x") { m_staggerAxis = StaggerAxis::X; } else if (attribString == "y") { m_staggerAxis = StaggerAxis::Y; } if ((m_orientation == Orientation::Staggered || m_orientation == Orientation::Hexagonal) && m_staggerAxis == StaggerAxis::None) { Logger::log("Map missing stagger axis property. Map not loaded.", Logger::Type::Error); return reset(); } attribString = mapNode.attribute("staggerindex").as_string(); if (attribString == "odd") { m_staggerIndex = StaggerIndex::Odd; } else if (attribString == "even") { m_staggerIndex = StaggerIndex::Even; } if ((m_orientation == Orientation::Staggered || m_orientation == Orientation::Hexagonal) && m_staggerIndex == StaggerIndex::None) { Logger::log("Map missing stagger index property. Map not loaded.", Logger::Type::Error); return reset(); } //colour property is optional attribString = mapNode.attribute("backgroundcolor").as_string(); if (!attribString.empty()) { m_backgroundColour = colourFromString(attribString); } //TODO do we need next object ID? technically we won't be creating //new objects outside of the scene in xygine. //parse all child nodes for (const auto& node : mapNode.children()) { std::string name = node.name(); if (name == "tileset") { m_tilesets.emplace_back(m_workingDirectory); m_tilesets.back().parse(node, this); } else if (name == "layer") { m_layers.emplace_back(std::make_unique(m_tileCount.x * m_tileCount.y)); m_layers.back()->parse(node); } else if (name == "objectgroup") { m_layers.emplace_back(std::make_unique()); m_layers.back()->parse(node, this); } else if (name == "imagelayer") { m_layers.emplace_back(std::make_unique(m_workingDirectory)); m_layers.back()->parse(node, this); } else if (name == "properties") { const auto& children = node.children(); for (const auto& child : children) { m_properties.emplace_back(); m_properties.back().parse(child); } } else if (name == "group") { m_layers.emplace_back(std::make_unique(m_workingDirectory, m_tileCount)); m_layers.back()->parse(node, this); } else { LOG("Unidentified name " + name + ": node skipped", Logger::Type::Warning); } } // fill animated tiles for easier lookup into map for(const auto& ts : m_tilesets) { for(const auto& tile : ts.getTiles()) { if (!tile.animation.frames.empty()) { m_animTiles[tile.ID + ts.getFirstGID()] = tile; } } } return true; } bool Map::reset() { m_orientation = Orientation::None; m_renderOrder = RenderOrder::None; m_tileCount = { 0u, 0u }; m_tileSize = { 0u, 0u }; m_hexSideLength = 0.f; m_staggerAxis = StaggerAxis::None; m_staggerIndex = StaggerIndex::None; m_backgroundColour = {}; m_workingDirectory = ""; m_tilesets.clear(); m_layers.clear(); m_properties.clear(); m_templateObjects.clear(); m_templateTilesets.clear(); return false; } ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/Map.hpp ================================================ /********************************************************************* Matt Marchant 2016 http://trederia.blogspot.com tmxlite - Zlib license. 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. *********************************************************************/ #pragma once #include "Tileset.hpp" #include "Layer.hpp" #include "Property.hpp" #include "Types.hpp" #include "Object.hpp" #include #include #include #include namespace tmx { /*! \brief Holds the xml version of the loaded map */ struct TMXLITE_EXPORT_API Version { //major/minor are apparently reserved by gcc std::uint16_t upper; std::uint16_t lower; Version(std::uint16_t maj = 0, std::uint16_t min = 0) : upper(maj), lower(min) {} }; enum class Orientation { Orthogonal, Isometric, Staggered, Hexagonal, None }; enum class RenderOrder { RightDown, RightUp, LeftDown, LeftUp, None }; enum class StaggerAxis { X, Y, None }; enum class StaggerIndex { Even, Odd, None }; /*! \brief Parser for TMX format tile maps. This class can be used to parse the XML format tile maps created with the Tiled map editor, providing an interface to create drawable and physics objects. Typical usage would be to create an instance of this class before calling load() providing a path to the *.tmx file to be loaded. Then layers or objects can be requested from the Map class to be interpreted as needed. */ class TMXLITE_EXPORT_API Map final { public: Map(); ~Map() = default; Map(const Map&) = delete; Map& operator = (const Map&) = delete; Map(Map&&) = default; Map& operator = (Map&&) = default; /*! \brief Attempts to parse the tilemap at the given location. \param std::string Path to map file to try to parse \returns true if map was parsed successfully else returns false. In debug mode this will attempt to log any errors to the console. */ bool load(const std::string&); /*! \brief Loads a map from a document stored in a string \param data A std::string containing the map data to load \param workingDir A std::string containing the working directory in which to find assets such as tile sets or images \returns true if successful, else false */ bool loadFromString(const std::string& data, const std::string& workingDir); /*! \brief Returns the version of the tile map last parsed. If no tile map has yet been parsed the version will read 0, 0 */ const Version& getVersion() const { return m_version; } /*! \brief Returns the orientation of the map if one is loaded, else returns None */ Orientation getOrientation() const { return m_orientation; } /*! \brief Returns the RenderOrder of the map if one is loaded, else returns None */ RenderOrder getRenderOrder() const { return m_renderOrder; } /*! \brief Returns the tile count of the map in the X and Y directions */ const Vector2u& getTileCount() const { return m_tileCount; } /*! \brief Returns the size of the tile grid in this map. Actual tile sizes may vary and will be extended / shrunk about the bottom left corner of the tile. */ const Vector2u& getTileSize() const { return m_tileSize; } /*! \brief Returns the bounds of the map */ FloatRect getBounds() const { return FloatRect(0.f, 0.f, static_cast(m_tileCount.x * m_tileSize.x), static_cast(m_tileCount.y * m_tileSize.y)); } /*! \brief Returns the length of an edge of a tile if a Hexagonal map is loaded. The length returned is in pixels of the straight edge running along the axis returned by getStaggerAxis(). If no map is loaded or the loaded map is not of Hexagonal orientation this function returns 0.f */ float getHexSideLength() const { return m_hexSideLength; } /*! \brief Stagger axis of the map. If either a Staggered or Hexagonal tile map is loaded this returns which axis the map is staggered along, else returns None. */ StaggerAxis getStaggerAxis() const { return m_staggerAxis; } /*! \brief Stagger Index of the loaded map. If a Staggered or Hexagonal map is loaded this returns whether the even or odd rows of tiles are staggered, otherwise it returns None. */ StaggerIndex getStaggerIndex() const { return m_staggerIndex; } /*! \brief Returns the background colour of the map. */ const Colour& getBackgroundColour() const { return m_backgroundColour; } /*! \brief Returns a reference to the vector of tile sets used by the map */ const std::vector& getTilesets() const { return m_tilesets; } /*! \brief Returns a reference to the vector containing the layer data. Layers are pointer-to-baseclass, the concrete type of which can be found via Layer::getType() \see Layer */ const std::vector& getLayers() const { return m_layers; } /*! \brief Returns a vector of Property objects loaded by the map */ const std::vector& getProperties() const { return m_properties; } /*! \brief Returns a Hashmap of all animated tiles accessible by TileID */ const std::map& getAnimatedTiles() const { return m_animTiles; } /*! \brief Returns the current working directory of the map. Images and other resources are loaded relative to this. */ const std::string& getWorkingDirectory() const { return m_workingDirectory; } /*! \brief Returns an unordered_map of template objects indexed by file name */ std::unordered_map& getTemplateObjects() { return m_templateObjects; } const std::unordered_map& getTemplateObjects() const { return m_templateObjects; } /*! \brief Returns an unordered_map of tilesets used by templated objects. If Object::getTilesetName() is not empty it can be used to retreive a tileset from this map. Otherwise the object's tileset can be found from in the map's global tilesets returned by getTilesets(). */ std::unordered_map& getTemplateTilesets() { return m_templateTilesets; } const std::unordered_map& getTemplateTilesets() const { return m_templateTilesets; } /*! \brief Returns true if this is in infinite tile map. Infinite maps store their tile data in for tile layers in chunks. If this is an infinite map use TileLayer::getChunks() to get tile IDs rather than TileLayer::getTiles(). \see TileLayer */ bool isInfinite() const { return m_infinite; } private: Version m_version; Orientation m_orientation; RenderOrder m_renderOrder; bool m_infinite; Vector2u m_tileCount; Vector2u m_tileSize; float m_hexSideLength; StaggerAxis m_staggerAxis; StaggerIndex m_staggerIndex; Colour m_backgroundColour; std::string m_workingDirectory; std::vector m_tilesets; std::vector m_layers; std::vector m_properties; std::map m_animTiles; std::unordered_map m_templateObjects; std::unordered_map m_templateTilesets; bool parseMapNode(const pugi::xml_node&); //always returns false so we can return this //on load failure bool reset(); }; } ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/Object.cpp ================================================ /********************************************************************* Matt Marchant 2016 http://trederia.blogspot.com tmxlite - Zlib license. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. *********************************************************************/ #include "Object.hpp" #include "FreeFuncs.hpp" #include "Map.hpp" #include "Tileset.hpp" #include "detail/pugixml.hpp" #include "detail/Log.hpp" #include using namespace tmx; Object::Object() : m_UID (0), m_rotation (0.f), m_tileID (0), m_visible (true), m_shape (Shape::Rectangle) { } //public void Object::parse(const pugi::xml_node& node, Map* map) { std::string attribString = node.name(); if (attribString != "object") { Logger::log("This not an Object node, parsing skipped.", Logger::Type::Error); return; } m_UID = node.attribute("id").as_int(); m_name = node.attribute("name").as_string(); m_type = node.attribute("type").as_string(); m_position.x = node.attribute("x").as_float(); m_AABB.left = m_position.x; m_position.y = node.attribute("y").as_float(); m_AABB.top = m_position.y; m_AABB.width = node.attribute("width").as_float(); m_AABB.height = node.attribute("height").as_float(); m_rotation = node.attribute("rotation").as_float(); m_tileID = node.attribute("gid").as_uint(); m_visible = node.attribute("visible").as_bool(true); for (const auto& child : node.children()) { attribString = child.name(); if (attribString == "properties") { for (const auto& p : child.children()) { m_properties.emplace_back(); m_properties.back().parse(p); } } else if (attribString == "ellipse") { m_shape = Shape::Ellipse; } else if (attribString == "point") { m_shape = Shape::Point; } else if (attribString == "polygon") { m_shape = Shape::Polygon; parsePoints(child); } else if (attribString == "polyline") { m_shape = Shape::Polyline; parsePoints(child); } else if (attribString == "text") { m_shape = Shape::Text; parseText(child); } } //parse templates last so we know which properties //ought to be overridden std::string templateStr = node.attribute("template").as_string(); if (!templateStr.empty() && map) { parseTemplate(templateStr, map); } } //private void Object::parsePoints(const pugi::xml_node& node) { if (node.attribute("points")) { std::string pointlist = node.attribute("points").as_string(); std::stringstream stream(pointlist); std::vector points; std::string pointstring; while (std::getline(stream, pointstring, ' ')) { points.push_back(pointstring); } //parse each pair into sf::vector2f for (unsigned int i = 0; i < points.size(); i++) { std::vector coords; std::stringstream coordstream(points[i]); float j; while (coordstream >> j) { coords.push_back(j); //TODO this should really ignore anything non-numeric if (coordstream.peek() == ',') { coordstream.ignore(); } } m_points.emplace_back(coords[0], coords[1]); } } else { Logger::log("Points for polygon or polyline object are missing", Logger::Type::Warning); } } void Object::parseText(const pugi::xml_node& node) { m_textData.bold = node.attribute("bold").as_bool(false); m_textData.colour = colourFromString(node.attribute("color").as_string("#FFFFFFFF")); m_textData.fontFamily = node.attribute("fontfamily").as_string(); m_textData.italic = node.attribute("italic").as_bool(false); m_textData.kerning = node.attribute("kerning").as_bool(true); m_textData.pixelSize = node.attribute("pixelsize").as_uint(16); m_textData.strikethough = node.attribute("strikeout").as_bool(false); m_textData.underline = node.attribute("underline").as_bool(false); m_textData.wrap = node.attribute("wrap").as_bool(false); std::string alignment = node.attribute("halign").as_string("left"); if (alignment == "left") { m_textData.hAlign = Text::HAlign::Left; } else if (alignment == "center") { m_textData.hAlign = Text::HAlign::Centre; } else if (alignment == "right") { m_textData.hAlign = Text::HAlign::Right; } alignment = node.attribute("valign").as_string("top"); if (alignment == "top") { m_textData.vAlign = Text::VAlign::Top; } else if (alignment == "center") { m_textData.vAlign = Text::VAlign::Centre; } else if (alignment == "bottom") { m_textData.vAlign = Text::VAlign::Bottom; } m_textData.content = node.text().as_string(); } void Object::parseTemplate(const std::string& path, Map* map) { assert(map); auto& templateObjects = map->getTemplateObjects(); auto& templateTilesets = map->getTemplateTilesets(); //load the template if not already loaded if (templateObjects.count(path) == 0) { auto templatePath = map->getWorkingDirectory() + "/" + path; pugi::xml_document doc; if (!doc.load_file(templatePath.c_str())) { Logger::log("Failed opening template file " + path, Logger::Type::Error); return; } auto templateNode = doc.child("template"); if (!templateNode) { Logger::log("Template node missing from " + path, Logger::Type::Error); return; } //if the template has a tileset load that (if not already loaded) std::string tilesetName; auto tileset = templateNode.child("tileset"); if (tileset) { tilesetName = tileset.attribute("source").as_string(); if (!tilesetName.empty() && templateTilesets.count(tilesetName) == 0) { templateTilesets.insert(std::make_pair(tilesetName, Tileset(map->getWorkingDirectory()))); templateTilesets.at(tilesetName).parse(tileset, map); } } //parse the object - don't pass the map pointer here so there's //no recursion if someone tried to get clever and put a template in a template auto obj = templateNode.child("object"); if (obj) { templateObjects.insert(std::make_pair(path, Object())); templateObjects[path].parse(obj, nullptr); templateObjects[path].m_tilesetName = tilesetName; } } //apply any non-overridden object properties from the template if (templateObjects.count(path) != 0) { const auto& obj = templateObjects[path]; if (m_AABB.width == 0) { m_AABB.width = obj.m_AABB.width; } if (m_AABB.height == 0) { m_AABB.height = obj.m_AABB.height; } m_tilesetName = obj.m_tilesetName; if (m_name.empty()) { m_name = obj.m_name; } if (m_type.empty()) { m_type = obj.m_type; } if (m_rotation == 0) { m_rotation = obj.m_rotation; } if (m_tileID == 0) { m_tileID = obj.m_tileID; } if (m_shape == Shape::Rectangle) { m_shape = obj.m_shape; } if (m_points.empty()) { m_points = obj.m_points; } //compare properties and only copy ones that don't exist for (const auto& p : obj.m_properties) { auto result = std::find_if(m_properties.begin(), m_properties.end(), [&p](const Property& a) { return a.getName() == p.getName(); }); if (result == m_properties.end()) { m_properties.push_back(p); } } if (m_shape == Shape::Text) { //check each text property and update as necessary //TODO this makes he assumption we prefer the template //properties over the default ones - this might not //actually be the case.... const auto& otherText = obj.m_textData; if (m_textData.fontFamily.empty()) { m_textData.fontFamily = otherText.fontFamily; } if (m_textData.pixelSize == 16) { m_textData.pixelSize = otherText.pixelSize; } //TODO this isn't actually right if we *want* to be false //and the template is set to true... if (m_textData.wrap == false) { m_textData.wrap = otherText.wrap; } if (m_textData.colour == Colour()) { m_textData.colour = otherText.colour; } if (m_textData.bold == false) { m_textData.bold = otherText.bold; } if (m_textData.italic == false) { m_textData.italic = otherText.italic; } if (m_textData.underline == false) { m_textData.underline = otherText.underline; } if (m_textData.strikethough == false) { m_textData.strikethough = otherText.strikethough; } if (m_textData.kerning == true) { m_textData.kerning = otherText.kerning; } if (m_textData.hAlign == Text::HAlign::Left) { m_textData.hAlign = otherText.hAlign; } if (m_textData.vAlign == Text::VAlign::Top) { m_textData.vAlign = otherText.vAlign; } if (m_textData.content.empty()) { m_textData.content = otherText.content; } } } } ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/Object.hpp ================================================ /********************************************************************* Matt Marchant 2016 - 2020 http://trederia.blogspot.com tmxlite - Zlib license. 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. *********************************************************************/ #pragma once #include "Config.hpp" #include "Property.hpp" #include "Types.hpp" #include #include namespace pugi { class xml_node; } namespace tmx { class Map; /*! \brief Contains the text information stored in a Text object. */ struct TMXLITE_EXPORT_API Text final { std::string fontFamily; std::uint32_t pixelSize = 16; //!< pixels, not points bool wrap = false; Colour colour; bool bold = false; bool italic = false; bool underline = false; bool strikethough = false; bool kerning = true; enum class HAlign { Left, Centre, Right }hAlign = HAlign::Left; enum class VAlign { Top, Centre, Bottom }vAlign = VAlign::Top; std::string content; //!< actual string content }; /*! \brief Objects are stored in ObjectGroup layers. Objects may be rectangular, elliptical, polygonal or a polyline. Rectangular and elliptical Objects have their size determined via the AABB, whereas polygon and polyline shapes are defined by a list of points. Objects are rectangular by default. Since version 1.0 Objects also support Text nodes. */ class TMXLITE_EXPORT_API Object final { public: enum class Shape { Rectangle, Ellipse, Point, Polygon, Polyline, Text }; Object(); /*! \brief Attempts to parse the given xml node and read the Object properties if it is valid. */ void parse(const pugi::xml_node&, Map*); /*! \brief Returns the unique ID of the Object */ std::uint32_t getUID() const { return m_UID; } /*! \brief Returns the name of the Object */ const std::string& getName() const { return m_name; } /*! \brief Returns the type of the Object, as defined in the editor */ const std::string& getType() const { return m_type; } /*! \brief Returns the position of the Object in pixels */ const Vector2f& getPosition() const { return m_position; } /*! \brief Returns the global Axis Aligned Bounding Box. The AABB is positioned via the left and top properties, and define the Object's width and height. This can be used to derive the shape of the Object if it is rectangular or elliptical. */ const FloatRect& getAABB() const { return m_AABB; } /*! \brief Returns the rotation of the Object in degrees clockwise */ float getRotation() const { return m_rotation; } /*! \brief Returns the global tile ID associated with the Object if there is one. This is used to draw the Object (and therefore the Object must be rectangular) */ uint32_t getTileID() const { return m_tileID; } /*! \brief Returns whether or not the Object is visible */ bool visible() const { return m_visible; } /*! \brief Returns the Shape type of the Object */ Shape getShape() const { return m_shape; } /*! \brief Returns a reference to the vector of points which make up the Object. If the Object is rectangular or elliptical then the vector will be empty. Point coordinates are in pixels, relative to the object position. */ const std::vector& getPoints() const { return m_points; } /*! \brief Returns a reference to the vector of properties belonging to the Object. */ const std::vector& getProperties() const { return m_properties; } /*! \brief Returns a Text struct containing information about any text this object may have, such as font data and formatting. If an object does not contain any text information this struct will be populated with default values. Use getShape() to determine if this object is in fact a text object. */ const Text& getText() const { return m_textData; } Text& getText() { return m_textData; } /*! \brief Returns the tileset name used by this object if it is derived from a template, else returns an empty string. If the string is not empty use it to index the unordered_map returned by Map::getTemplateTilesets() */ const std::string& getTilesetName() const { return m_tilesetName; } private: std::uint32_t m_UID; std::string m_name; std::string m_type; Vector2f m_position; FloatRect m_AABB; float m_rotation; std::uint32_t m_tileID; bool m_visible; Shape m_shape; std::vector m_points; std::vector m_properties; Text m_textData; std::string m_tilesetName; void parsePoints(const pugi::xml_node&); void parseText(const pugi::xml_node&); void parseTemplate(const std::string&, Map*); }; } ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/ObjectGroup.cpp ================================================ /********************************************************************* Matt Marchant 2016 http://trederia.blogspot.com tmxlite - Zlib license. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. *********************************************************************/ #include "FreeFuncs.hpp" #include "ObjectGroup.hpp" #include "detail/pugixml.hpp" #include "detail/Log.hpp" using namespace tmx; ObjectGroup::ObjectGroup() : m_colour (127, 127, 127, 255), m_drawOrder (DrawOrder::TopDown) { } //public void ObjectGroup::parse(const pugi::xml_node& node, Map* map) { assert(map); std::string attribString = node.name(); if (attribString != "objectgroup") { Logger::log("Node was not an object group, node will be skipped.", Logger::Type::Error); return; } setName(node.attribute("name").as_string()); attribString = node.attribute("color").as_string(); if (!attribString.empty()) { m_colour = colourFromString(attribString); } setOpacity(node.attribute("opacity").as_float(1.f)); setVisible(node.attribute("visible").as_bool(true)); setOffset(node.attribute("offsetx").as_int(), node.attribute("offsety").as_int()); setSize(node.attribute("width").as_uint(), node.attribute("height").as_uint()); attribString = node.attribute("draworder").as_string(); if (attribString == "index") { m_drawOrder = DrawOrder::Index; } for (const auto& child : node.children()) { attribString = child.name(); if (attribString == "properties") { for (const auto& p : child) { m_properties.emplace_back(); m_properties.back().parse(p); } } else if (attribString == "object") { m_objects.emplace_back(); m_objects.back().parse(child, map); } } } ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/ObjectGroup.hpp ================================================ /********************************************************************* Matt Marchant 2016 - 2019 http://trederia.blogspot.com tmxlite - Zlib license. 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. *********************************************************************/ #pragma once #include "Config.hpp" #include "Layer.hpp" #include "Object.hpp" #include namespace tmx { /*! \brief ObjectGroup layers contain a series of Objects which may be made up of shapes or images. */ class TMXLITE_EXPORT_API ObjectGroup final : public Layer { public: enum class DrawOrder { Index, //< objects should be drawn in the order in which they appear TopDown //< objects should be drawn sorted by their Y position }; ObjectGroup(); ~ObjectGroup() = default; Type getType() const override { return Layer::Type::Object; } void parse(const pugi::xml_node&, Map*) override; /*! \brief Returns the colour associated with this layer */ const Colour& getColour() const { return m_colour; } /*! \brief Returns the DrawOrder for the objects in this group. Defaults to TopDown, where Objects are drawn sorted by Y position */ DrawOrder getDrawOrder() const { return m_drawOrder; } /*! \brief Returns a reference to the vector of properties for the ObjectGroup */ const std::vector& getProperties() const { return m_properties; } /*! \brief Returns a reference to the vector of Objects which belong to the group */ const std::vector& getObjects() const { return m_objects; } private: Colour m_colour; DrawOrder m_drawOrder; std::vector m_properties; std::vector m_objects; }; template <> inline ObjectGroup& Layer::getLayerAs() { assert(getType() == Type::Object); return *dynamic_cast(this); } } ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/Property.cpp ================================================ /********************************************************************* Matt Marchant 2016 http://trederia.blogspot.com tmxlite - Zlib license. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. *********************************************************************/ #include "Property.hpp" #include "detail/pugixml.hpp" #include "detail/Log.hpp" #include "FreeFuncs.hpp" using namespace tmx; Property::Property() : m_type(Type::Undef) { } //public void Property::parse(const pugi::xml_node& node) { std::string attribData = node.name(); if (attribData != "property") { Logger::log("Node was not a valid property, node will be skipped", Logger::Type::Error); return; } m_name = node.attribute("name").as_string(); attribData = node.attribute("type").as_string("string"); if (attribData == "bool") { attribData = node.attribute("value").as_string("false"); m_boolValue = (attribData == "true"); m_type = Type::Boolean; return; } else if (attribData == "int") { m_intValue = node.attribute("value").as_int(0); m_type = Type::Int; return; } else if (attribData == "float") { m_floatValue = node.attribute("value").as_float(0.f); m_type = Type::Float; return; } else if (attribData == "string") { m_stringValue = node.attribute("value").as_string(); //if value is empty, try getting the child value instead //as this is how multiline string properties are stored. if(m_stringValue.empty()) { m_stringValue = node.child_value(); } m_type = Type::String; return; } else if (attribData == "color") { m_colourValue = colourFromString(node.attribute("value").as_string("#FFFFFFFF")); m_type = Type::Colour; return; } else if (attribData == "file") { m_stringValue = node.attribute("value").as_string(); m_type = Type::File; return; } } ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/Property.hpp ================================================ /********************************************************************* Matt Marchant 2016 http://trederia.blogspot.com tmxlite - Zlib license. 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. *********************************************************************/ #pragma once #include "Config.hpp" #include "Types.hpp" #include #include namespace pugi { class xml_node; } namespace tmx { /*! \brief Represents a custom property. Tiles, objects and layers of a tmx map may have custom properties assigned to them. This class represents a single property and provides access to its value, the type of which can be determined with getType() */ class TMXLITE_EXPORT_API Property final { public: enum class Type { Boolean, Float, Int, String, Colour, File, Undef }; Property(); ~Property() = default; /*! \brief Attempts to parse the given node as a property */ void parse(const pugi::xml_node&); /*! \brief Returns the type of data stored in the property. This should generally be called first before trying to read the proprty value, as reading the incorrect type will lead to undefined behaviour. */ Type getType() const { return m_type; } /*! \brief Returns the name of this property */ const std::string& getName() const { return m_name; } /*! \brief Returns the property's value as a boolean */ bool getBoolValue() const { assert(m_type == Type::Boolean); return m_boolValue; } /*! \brief Returns the property's value as a float */ float getFloatValue() const { assert(m_type == Type::Float); return m_floatValue; } /*! \brief Returns the property's value as an integer */ int getIntValue() const { assert(m_type == Type::Int); return m_intValue; } /*! \brief Returns the property's value as a string */ const std::string& getStringValue() const { assert(m_type == Type::String); return m_stringValue; } /*! \brief Returns the property's value as a Colour struct */ const Colour& getColourValue() const { assert(m_type == Type::Colour); return m_colourValue; } /*! \brief Returns the file path property as a string, relative to the map file */ const std::string& getFileValue() const { assert(m_type == Type::File); return m_stringValue; } private: union { bool m_boolValue; float m_floatValue; int m_intValue; }; std::string m_stringValue; std::string m_name; Colour m_colourValue; Type m_type; }; } ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/TileLayer.cpp ================================================ /********************************************************************* Matt Marchant 2016 http://trederia.blogspot.com tmxlite - Zlib license. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. *********************************************************************/ #include "FreeFuncs.hpp" #include "TileLayer.hpp" #include "detail/pugixml.hpp" #include "detail/Log.hpp" #include using namespace tmx; TileLayer::TileLayer(std::size_t tileCount) : m_tileCount (tileCount) { m_tiles.reserve(tileCount); } //public void TileLayer::parse(const pugi::xml_node& node, Map*) { std::string attribName = node.name(); if (attribName != "layer") { Logger::log("node not a layer node, skipped parsing", Logger::Type::Error); return; } setName(node.attribute("name").as_string()); setOpacity(node.attribute("opacity").as_float(1.f)); setVisible(node.attribute("visible").as_bool(true)); setOffset(node.attribute("offsetx").as_int(), node.attribute("offsety").as_int()); setSize(node.attribute("width").as_uint(), node.attribute("height").as_uint()); for (const auto& child : node.children()) { attribName = child.name(); if (attribName == "data") { attribName = child.attribute("encoding").as_string(); if (attribName == "base64") { parseBase64(child); } else if (attribName == "csv") { parseCSV(child); } else { parseUnencoded(child); } } else if (attribName == "properties") { for (const auto& p : child.children()) { addProperty(p); } } } } //private void TileLayer::parseBase64(const pugi::xml_node& node) { auto processDataString = [](std::string dataString, std::size_t tileCount, bool compressed)->std::vector { std::stringstream ss; ss << dataString; ss >> dataString; dataString = base64_decode(dataString); std::size_t expectedSize = tileCount * 4; //4 bytes per tile std::vector byteData; byteData.reserve(expectedSize); if (compressed) { //unzip std::size_t dataSize = dataString.length() * sizeof(unsigned char); if (!decompress(dataString.c_str(), byteData, dataSize, expectedSize)) { LOG("Failed to decompress layer data, node skipped.", Logger::Type::Error); return {}; } } else { byteData.insert(byteData.end(), dataString.begin(), dataString.end()); } //data stream is in bytes so we need to OR into 32 bit values std::vector IDs; IDs.reserve(tileCount); for (auto i = 0u; i < expectedSize - 3u; i += 4u) { std::uint32_t id = byteData[i] | byteData[i + 1] << 8 | byteData[i + 2] << 16 | byteData[i + 3] << 24; IDs.push_back(id); } return IDs; }; std::string data = node.text().as_string(); if (data.empty()) { //check for chunk nodes auto dataCount = 0; for (const auto& childNode : node.children()) { std::string childName = childNode.name(); if (childName == "chunk") { std::string dataString = childNode.text().as_string(); if (!dataString.empty()) { Chunk chunk; chunk.position.x = childNode.attribute("x").as_int(); chunk.position.y = childNode.attribute("y").as_int(); chunk.size.x = childNode.attribute("width").as_int(); chunk.size.y = childNode.attribute("height").as_int(); auto IDs = processDataString(dataString, (chunk.size.x * chunk.size.y), node.attribute("compression")); if (!IDs.empty()) { createTiles(IDs, chunk.tiles); m_chunks.push_back(chunk); dataCount++; } } } } if (dataCount == 0) { Logger::log("Layer " + getName() + " has no layer data. Layer skipped.", Logger::Type::Error); return; } } else { auto IDs = processDataString(data, m_tileCount, node.attribute("compression")); createTiles(IDs, m_tiles); } } void TileLayer::parseCSV(const pugi::xml_node& node) { auto processDataString = [](const std::string dataString, std::size_t tileCount)->std::vector { std::vector IDs; IDs.reserve(tileCount); const char* ptr = dataString.c_str(); while (true) { char* end; auto res = std::strtoul(ptr, &end, 10); if (end == ptr) break; ptr = end; IDs.push_back(res); if (*ptr == ',') ++ptr; } return IDs; }; std::string data = node.text().as_string(); if (data.empty()) { //check for chunk nodes auto dataCount = 0; for (const auto& childNode : node.children()) { std::string childName = childNode.name(); if (childName == "chunk") { std::string dataString = childNode.text().as_string(); if (!dataString.empty()) { Chunk chunk; chunk.position.x = childNode.attribute("x").as_int(); chunk.position.y = childNode.attribute("y").as_int(); chunk.size.x = childNode.attribute("width").as_int(); chunk.size.y = childNode.attribute("height").as_int(); auto IDs = processDataString(dataString, chunk.size.x * chunk.size.y); if (!IDs.empty()) { createTiles(IDs, chunk.tiles); m_chunks.push_back(chunk); dataCount++; } } } } if (dataCount == 0) { Logger::log("Layer " + getName() + " has no layer data. Layer skipped.", Logger::Type::Error); return; } } else { createTiles(processDataString(data, m_tileCount), m_tiles); } } void TileLayer::parseUnencoded(const pugi::xml_node& node) { std::string attribName; std::vector IDs; IDs.reserve(m_tileCount); for (const auto& child : node.children()) { attribName = child.name(); if (attribName == "tile") { IDs.push_back(child.attribute("gid").as_uint()); } } createTiles(IDs, m_tiles); } void TileLayer::createTiles(const std::vector& IDs, std::vector& destination) { //LOG(IDs.size() != m_tileCount, "Layer tile count does not match expected size. Found: " // + std::to_string(IDs.size()) + ", expected: " + std::to_string(m_tileCount)); static const std::uint32_t mask = 0xf0000000; for (const auto& id : IDs) { destination.emplace_back(); destination.back().flipFlags = ((id & mask) >> 28); destination.back().ID = id & ~mask; } } ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/TileLayer.hpp ================================================ /********************************************************************* Matt Marchant 2016 - 2019 http://trederia.blogspot.com tmxlite - Zlib license. 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. *********************************************************************/ #pragma once #include "Layer.hpp" #include "Types.hpp" namespace tmx { /*! \brief A layer made up from a series of tile sets */ class TMXLITE_EXPORT_API TileLayer final : public Layer { public: /*! \brief Tile information for a layer */ struct Tile final { std::uint32_t ID = 0; //!< Global ID of the tile std::uint8_t flipFlags = 0; //!< Flags marking if the tile should be flipped when drawn }; /*! \brief Represents a chunk of tile data, if this is an infinite map */ struct Chunk final { Vector2i position; // tiles; }; /*! \brief Flags used to tell if a tile is flipped when drawn */ enum FlipFlag { Horizontal = 0x8, Vertical = 0x4, Diagonal = 0x2 }; explicit TileLayer(std::size_t); ~TileLayer() = default; Type getType() const override { return Layer::Type::Tile; } void parse(const pugi::xml_node&, Map*) override; /*! \brief Returns the list of tiles used to make up the layer If this is empty then the map is most likely infinite, in which case the tile data is stored in chunks. \see getChunks() */ const std::vector& getTiles() const { return m_tiles; } /*! \brief Returns a vector of chunks which make up this layer if the map is set to infinite. This will be empty if the map is not infinite. \see getTiles() */ const std::vector& getChunks() const { return m_chunks; } private: std::vector m_tiles; std::vector m_chunks; std::size_t m_tileCount; void parseBase64(const pugi::xml_node&); void parseCSV(const pugi::xml_node&); void parseUnencoded(const pugi::xml_node&); void createTiles(const std::vector&, std::vector& destination); }; template <> inline TileLayer& Layer::getLayerAs() { assert(getType() == Type::Tile); return *dynamic_cast(this); } } ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/Tileset.cpp ================================================ /********************************************************************* Matt Marchant 2016 http://trederia.blogspot.com tmxlite - Zlib license. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. *********************************************************************/ #include "Tileset.hpp" #include "detail/pugixml.hpp" #include "FreeFuncs.hpp" #include "detail/Log.hpp" #include using namespace tmx; Tileset::Tileset(const std::string& workingDir) : m_workingDir (workingDir), m_firstGID (0), m_spacing (0), m_margin (0), m_tileCount (0), m_columnCount (0), m_transparencyColour (0, 0, 0, 0) { } //public void Tileset::parse(pugi::xml_node node, Map* map) { assert(map); std::string attribString = node.name(); if (attribString != "tileset") { Logger::log(attribString + ": not a tileset node! Node will be skipped.", Logger::Type::Warning); return; } m_firstGID = node.attribute("firstgid").as_int(); if (m_firstGID == 0) { Logger::log("Invalid first GID in tileset. Tileset node skipped.", Logger::Type::Warning); return; } pugi::xml_document tsxDoc; //need to keep this in scope if (node.attribute("source")) { //parse TSX doc std::string path = node.attribute("source").as_string(); path = resolveFilePath(path, m_workingDir); //as the TSX file now dictates the image path, the working //directory is now that of the tsx file auto position = path.find_last_of('/'); if (position != std::string::npos) { m_workingDir = path.substr(0, position); } else { m_workingDir = ""; } //see if doc can be opened auto result = tsxDoc.load_file(path.c_str()); if (!result) { Logger::log("Failed opening tsx file for tile set, tile set will be skipped", Logger::Type::Error); return reset(); } //if it can then replace the current node with tsx node node = tsxDoc.child("tileset"); if (!node) { Logger::log("tsx file does not contain a tile set node, tile set will be skipped", Logger::Type::Error); return reset(); } } m_name = node.attribute("name").as_string(); LOG("found tile set " + m_name, Logger::Type::Info); m_tileSize.x = node.attribute("tilewidth").as_int(); m_tileSize.y = node.attribute("tileheight").as_int(); if (m_tileSize.x == 0 || m_tileSize.y == 0) { Logger::log("Invalid tile size found in tile set node. Node will be skipped.", Logger::Type::Error); return reset(); } m_spacing = node.attribute("spacing").as_int(); m_margin = node.attribute("margin").as_int(); m_tileCount = node.attribute("tilecount").as_int(); m_columnCount = node.attribute("columns").as_int(); const auto& children = node.children(); for (const auto& node : children) { std::string name = node.name(); if (name == "image") { //TODO this currently doesn't cover embedded images //mostly because I can't figure out how to export them //from the Tiled editor... but also resource handling //should be handled by the renderer, not the parser. attribString = node.attribute("source").as_string(); if (attribString.empty()) { Logger::log("Tileset image node has missing source property, tile set not loaded", Logger::Type::Error); return reset(); } m_imagePath = resolveFilePath(attribString, m_workingDir); if (node.attribute("trans")) { attribString = node.attribute("trans").as_string(); m_transparencyColour = colourFromString(attribString); } if (node.attribute("width") && node.attribute("height")) { m_imageSize.x = node.attribute("width").as_int(); m_imageSize.y = node.attribute("height").as_int(); } } else if (name == "tileoffset") { parseOffsetNode(node); } else if (name == "properties") { parsePropertyNode(node); } else if (name == "terraintypes") { parseTerrainNode(node); } else if (name == "tile") { parseTileNode(node, map); } } // If the tsx file does not declare every tile, we create the missing ones if (m_tiles.size() != getTileCount()) { for (std::uint32_t ID = 0 ; ID < getTileCount() ; ID++) { createMissingTile(ID); } } //sort these just to make sure when we request last GID we get the corrtect value std::sort(m_tiles.begin(), m_tiles.end(), [](const Tile& t1, const Tile& t2) {return t1.ID < t2.ID; }); } std::uint32_t Tileset::getLastGID() const { assert(!m_tiles.empty()); return m_firstGID + m_tiles.back().ID; } const Tileset::Tile* Tileset::getTile(std::uint32_t id) const { if (!hasTile(id)) { return nullptr; } //corrects the ID. Indices and IDs are different. id = (getLastGID() - m_firstGID) - (getLastGID() - id); const auto itr = std::find_if(m_tiles.begin(), m_tiles.end(), [id](const Tile& tile) { return tile.ID == id; }); return (itr == m_tiles.end()) ? nullptr : &(*itr); } //private void Tileset::reset() { m_firstGID = 0; m_source = ""; m_name = ""; m_tileSize = { 0,0 }; m_spacing = 0; m_margin = 0; m_tileCount = 0; m_columnCount = 0; m_tileOffset = { 0,0 }; m_properties.clear(); m_imagePath = ""; m_transparencyColour = { 0, 0, 0, 0 }; m_terrainTypes.clear(); m_tiles.clear(); } void Tileset::parseOffsetNode(const pugi::xml_node& node) { m_tileOffset.x = node.attribute("x").as_int(); m_tileOffset.y = node.attribute("y").as_int(); } void Tileset::parsePropertyNode(const pugi::xml_node& node) { const auto& children = node.children(); for (const auto& child : children) { m_properties.emplace_back(); m_properties.back().parse(child); } } void Tileset::parseTerrainNode(const pugi::xml_node& node) { const auto& children = node.children(); for (const auto& child : children) { std::string name = child.name(); if (name == "terrain") { m_terrainTypes.emplace_back(); auto& terrain = m_terrainTypes.back(); terrain.name = child.attribute("name").as_string(); terrain.tileID = child.attribute("tile").as_int(); auto properties = child.child("properties"); if (properties) { for (const auto& p : properties) { name = p.name(); if (name == "property") { terrain.properties.emplace_back(); terrain.properties.back().parse(p); } } } } } } void Tileset::parseTileNode(const pugi::xml_node& node, Map* map) { assert(map); Tile tile; tile.ID = node.attribute("id").as_int(); if (node.attribute("terrain")) { std::string data = node.attribute("terrain").as_string(); bool lastWasChar = true; std::size_t idx = 0u; for (auto i = 0u; i < data.size() && idx < tile.terrainIndices.size(); ++i) { if (isdigit(data[i])) { tile.terrainIndices[idx++] = std::atoi(&data[i]); lastWasChar = false; } else { if (!lastWasChar) { lastWasChar = true; } else { tile.terrainIndices[idx++] = -1; lastWasChar = false; } } } if (lastWasChar) { tile.terrainIndices[idx] = -1; } } tile.probability = node.attribute("probability").as_int(100); tile.type = node.attribute("type").as_string(); //by default we set the tile's values as in an Image tileset tile.imagePath = m_imagePath; tile.imageSize = m_tileSize; if (m_columnCount != 0) { std::int32_t rowIndex = tile.ID % m_columnCount; std::int32_t columnIndex = tile.ID / m_columnCount; tile.imagePosition.x = m_margin + rowIndex * (m_tileSize.x + m_spacing); tile.imagePosition.y = m_margin + columnIndex * (m_tileSize.y + m_spacing); } const auto& children = node.children(); for (const auto& child : children) { std::string name = child.name(); if (name == "properties") { for (const auto& prop : child.children()) { tile.properties.emplace_back(); tile.properties.back().parse(prop); } } else if (name == "objectgroup") { tile.objectGroup.parse(child, map); } else if (name == "image") { std::string attribString = child.attribute("source").as_string(); if (attribString.empty()) { Logger::log("Tile image path missing", Logger::Type::Warning); continue; } tile.imagePath = resolveFilePath(attribString, m_workingDir); tile.imagePosition = tmx::Vector2u(0, 0); if (child.attribute("trans")) { attribString = child.attribute("trans").as_string(); m_transparencyColour = colourFromString(attribString); } if (child.attribute("width")) { tile.imageSize.x = child.attribute("width").as_uint(); } if (child.attribute("height")) { tile.imageSize.y = child.attribute("height").as_uint(); } } else if (name == "animation") { for (const auto& frameNode : child.children()) { Tile::Animation::Frame frame; frame.duration = frameNode.attribute("duration").as_int(); frame.tileID = frameNode.attribute("tileid").as_int() + m_firstGID; tile.animation.frames.push_back(frame); } } } m_tiles.push_back(tile); } void Tileset::createMissingTile(std::uint32_t ID) { // First, we check if the tile does not yet exist for (auto &tile : m_tiles) { if (tile.ID == ID) return; } Tile tile; tile.ID = ID; tile.imagePath = m_imagePath; tile.imageSize = m_tileSize; std::int32_t rowIndex = ID % m_columnCount; std::int32_t columnIndex = ID / m_columnCount; tile.imagePosition.x = m_margin + rowIndex * (m_tileSize.x + m_spacing); tile.imagePosition.y = m_margin + columnIndex * (m_tileSize.y + m_spacing); m_tiles.push_back(tile); } ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/Tileset.hpp ================================================ /********************************************************************* Matt Marchant 2016 http://trederia.blogspot.com tmxlite - Zlib license. 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. *********************************************************************/ #pragma once #include "Config.hpp" #include "Property.hpp" #include "ObjectGroup.hpp" #include #include #include namespace pugi { class xml_node; } namespace tmx { class Map; /*! \brief Represents a Tileset node as loaded from a *.tmx format tile map via the tmx::Map class. */ class TMXLITE_EXPORT_API Tileset final { public: explicit Tileset(const std::string& workingDir); ~Tileset() = default; /*! \brief Any tiles within a tile set which have special data associated with them such as animation or terrain information will have one of these stored in the tile set. */ struct Tile final { std::uint32_t ID = 0; std::array terrainIndices{}; std::uint32_t probability = 100; /*! \brief a group of frames which make up an animation */ struct Animation final { /*! \brief A frame within an animation */ struct Frame final { std::uint32_t tileID = 0; std::uint32_t duration = 0; bool operator == (const Frame& other) const { return (this == &other) || (tileID == other.tileID && duration == other.duration); } bool operator != (const Frame& other) const { return !(*this == other); } }; std::vector frames; }animation; std::vector properties; ObjectGroup objectGroup; std::string imagePath; Vector2u imageSize; /*! \brief The position of the tile within the image. */ Vector2u imagePosition; std::string type; }; /*! \brief Terrain information with which one or more tiles may be associated. */ struct Terrain final { std::string name; std::uint32_t tileID = -1; std::vector properties; }; /*! \brief Attempts to parse the given xml node. If node parsing fails an error is printed in the console and the Tileset remains in an uninitialised state. */ void parse(pugi::xml_node, Map*); /*! \brief Returns the first GID of this tile set. This the ID of the first tile in the tile set, so that each tile set guarantees a unique set of IDs */ std::uint32_t getFirstGID() const { return m_firstGID; } /*! \brief Returns the last GID of this tile set. This is the ID of the last tile in the tile set. */ std::uint32_t getLastGID() const; /*! \brief Returns the name of this tile set. */ const std::string& getName() const { return m_name; } /*! \brief Returns the width and height of a tile in the tile set, in pixels. */ const Vector2u& getTileSize() const { return m_tileSize; } /*! \brief Returns the spacing, in pixels, between each tile in the set */ std::uint32_t getSpacing() const { return m_spacing; } /*! \brief Returns the margin, in pixels, around each tile in the set */ std::uint32_t getMargin() const { return m_margin; } /*! \brief Returns the number of tiles in the tile set */ std::uint32_t getTileCount() const { return m_tileCount; } /*! \brief Returns the number of columns which make up the tile set. This is used when rendering collection of images sets */ std::uint32_t getColumnCount() const { return m_columnCount; } /*! \brief Returns the tile offset in pixels. Tile will draw tiles offset from the top left using this value. */ const Vector2u& getTileOffset() const { return m_tileOffset; } /*! \brief Returns a reference to the list of Property objects for this tile set */ const std::vector& getProperties() const { return m_properties; } /*! \brief Returns the file path to the tile set image, relative to the working directory. Use this to load the texture required by whichever method you choose to render the map. */ const std::string getImagePath() const { return m_imagePath; } /*! \brief Returns the size of the tile set image in pixels. */ const Vector2u& getImageSize() const { return m_imageSize; } /*! \brief Returns the colour used by the tile map image to represent transparency. By default this is a transparent colour (0, 0, 0, 0) */ const Colour& getTransparencyColour() const { return m_transparencyColour; } /*! \brief Returns true if the image used by this tileset specifically requests a colour to use as transparency. */ bool hasTransparency() const { return m_hasTransparency; } /*! \brief Returns a vector of Terrain types associated with one or more tiles within this tile set */ const std::vector& getTerrainTypes() const { return m_terrainTypes; } /*! \brief Returns a reference to the vector of tile data used by tiles which make up this tile set. */ const std::vector& getTiles() const { return m_tiles; } /*! \brief Checks if a tiled ID is in the range of the first ID and the last ID \param id Tile ID \return */ bool hasTile(std::uint32_t id) const { return id >= m_firstGID && id <= getLastGID(); }; /*! \brief queries tiles and returns a tile with the given ID. Checks if the TileID is part of the Tileset with `hasTile(id)` \param id Tile ID. The Tile ID will be corrected internally. \return In case of a success it returns the correct tile. In terms of failure it will return a nullptr. */ const Tile* getTile(std::uint32_t id) const; private: std::string m_workingDir; std::uint32_t m_firstGID; std::string m_source; std::string m_name; Vector2u m_tileSize; std::uint32_t m_spacing; std::uint32_t m_margin; std::uint32_t m_tileCount; std::uint32_t m_columnCount; Vector2u m_tileOffset; std::vector m_properties; std::string m_imagePath; Vector2u m_imageSize; Colour m_transparencyColour; bool m_hasTransparency; std::vector m_terrainTypes; std::vector m_tiles; void reset(); void parseOffsetNode(const pugi::xml_node&); void parsePropertyNode(const pugi::xml_node&); void parseTerrainNode(const pugi::xml_node&); void parseTileNode(const pugi::xml_node&, Map*); void createMissingTile(std::uint32_t ID); }; } ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/Types.hpp ================================================ /********************************************************************* Matt Marchant 2016 http://trederia.blogspot.com tmxlite - Zlib license. 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. *********************************************************************/ #pragma once #include "Config.hpp" #include namespace tmx { /*! \brief Two dimensional vector used to store points and positions */ template struct Vector2 final { Vector2() : x(0), y(0) {} Vector2(T x, T y) :x(x), y(y) {} T x, y; }; using Vector2f = Vector2; using Vector2i = Vector2; using Vector2u = Vector2; template Vector2 operator + (const Vector2& l, const Vector2& r); template Vector2& operator += (Vector2& l, const Vector2& r); template Vector2 operator - (const Vector2& l, const Vector2& r); template Vector2& operator -= (Vector2& l, const Vector2& r); template Vector2 operator * (const Vector2& l, const Vector2& r); template Vector2& operator *= (Vector2& l, const Vector2& r); template Vector2 operator * (const Vector2& l, T r); template Vector2& operator *= (Vector2& l, T r); template Vector2 operator / (const Vector2& l, const Vector2& r); template Vector2& operator /= (Vector2& l, const Vector2& r); template Vector2 operator / (const Vector2& l, T r); template Vector2& operator /= (Vector2& l, T r); #include "Types.inl" /*! \brief Describes a rectangular area, such as an AABB (axis aligned bounding box) */ template struct Rectangle final { Rectangle() : left(0), top(0), width(0), height(0) {} Rectangle(T l, T t, T w, T h) : left(l), top(t), width(w), height(h) {} Rectangle(Vector2 position, Vector2 size) : left(position.x), top(position.y), width(size.x), height(size.y) {} T left, top, width, height; }; using FloatRect = Rectangle; using IntRect = Rectangle; /*! \brief Contains the red, green, blue and alpha values of a colour in the range 0 - 255. */ struct TMXLITE_EXPORT_API Colour final { Colour(std::uint8_t red = 0, std::uint8_t green = 0, std::uint8_t blue = 0, std::uint8_t alpha = 255) : r(red), g(green), b(blue), a(alpha) {} std::uint8_t r, g, b, a; bool operator == (const Colour& other) { return other.r == r && other.g == g && other.b == b && other.a == a; } bool operator != (const Colour& other) { return !(*this == other); } }; } ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/Types.inl ================================================ /********************************************************************* Matt Marchant 2016-2017 http://trederia.blogspot.com tmxlite - Zlib license. 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. *********************************************************************/ template Vector2 operator + (const Vector2& l, const Vector2& r) { return { l.x + r.x, l.y + r.y }; } template Vector2& operator += (Vector2& l, const Vector2& r) { l.x += r.x; l.y += r.y; return l; } template Vector2 operator - (const Vector2& l, const Vector2& r) { return { l.x - r.x, l.y - r.y }; } template Vector2& operator -= (Vector2& l, const Vector2& r) { l.x -= r.x; l.y -= r.y; return l; } template Vector2 operator * (const Vector2& l, const Vector2& r) { return { l.x * r.x, l.y * r.y }; } template Vector2& operator *= (Vector2& l, const Vector2& r) { l.x *= r.x; l.y *= r.y; return l; } template Vector2 operator * (const Vector2& l, T r) { return { l.x * r, l.y * r }; } template Vector2& operator *= (Vector2& l, T r) { l.x *= r; l.y *= r; return l; } template Vector2 operator / (const Vector2& l, const Vector2& r) { return { l.x / r.x, l.y / r.y }; } template Vector2& operator /= (Vector2& l, const Vector2& r) { l.x /= r.x; l.y /= r.y; return l; } template Vector2 operator / (const Vector2& l, T r) { return { l.x / r, l.y / r }; } template Vector2& operator /= (Vector2& l, T r) { l.x /= r; l.y /= r; return l; } ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/detail/Android.hpp ================================================ /********************************************************************* Matt Marchant 2016 http://trederia.blogspot.com tmxlite - Zlib license. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. *********************************************************************/ #ifndef ANDROID_INC_HPP_ #define ANDROID_INC_HPP_ #ifdef __ANDROID__ #include #include #include namespace std { template std::string to_string(T value) { std::ostringstream os; os << value; return os.str(); } } #define STOI(str) std::strtol(str.c_str(), 0, 10) #else #define STOI(str) std::stoi(str) #endif // __ANDROID__ #endif // ANDROID_INC_HPP_ ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/detail/Log.hpp ================================================ /********************************************************************* Matt Marchant 2016 http://trederia.blogspot.com tmxlite - Zlib license. 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. *********************************************************************/ //flexible logging class, based on code at https://github.com/fallahn/xygine #ifndef TMXLITE_LOGGER_HPP_ #define TMXLITE_LOGGER_HPP_ #include #include #include #include #include #include #include #ifdef _MSC_VER #define NOMINMAX #include #endif //_MSC_VER #ifdef __ANDROID__ #include #define LOG_TAG "TMXlite-Debug" //#define ALOG(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) #endif // __ANDROID__ namespace tmx { /*! \brief Class allowing messages to be logged to a combination of one or more destinations such as the console, log file or output window in Visual Studio */ class Logger final { public: enum class Output { Console, File, All }; enum class Type { Info, Warning, Error }; /*! \brief Logs a message to a given destination. \param message Message to log \param type Whether this message gets tagged as information, a warning or an error \param output Destination for the message. Can be the console via cout, a log file on disk, or both */ static void log(const std::string& message, Type type = Type::Info, Output output = Output::Console) { std::string outstring; switch (type) { case Type::Info: default: outstring = "INFO: " + message; break; case Type::Error: outstring = "ERROR: " + message; break; case Type::Warning: outstring = "WARNING: " + message; break; } if (output == Output::Console || output == Output::All) { if (type == Type::Error) { #ifdef __ANDROID__ int outstringLength = outstring.length(); char outstring_chararray[outstringLength+1]; strcpy(outstring_chararray, outstring.c_str()); LOGE("%s",outstring_chararray); #endif std::cerr << outstring << std::endl; }else{ #ifdef __ANDROID__ int outstringLength = outstring.length(); char outstring_chararray[outstringLength+1]; strcpy(outstring_chararray, outstring.c_str()); LOGI("%s", outstring_chararray); #endif std::cout << outstring << std::endl; } const std::size_t maxBuffer = 30; buffer().push_back(outstring); if (buffer().size() > maxBuffer)buffer().pop_front(); //no majick here pl0x updateOutString(maxBuffer); #ifdef _MSC_VER outstring += "\n"; OutputDebugStringA(outstring.c_str()); #endif //_MSC_VER } if (output == Output::File || output == Output::All) { //output to a log file std::ofstream file("output.log", std::ios::app); if (file.good()) { #ifndef __ANDROID__ std::time_t time = std::time(nullptr); auto tm = *std::localtime(&time); //put_time isn't implemented by the ndk versions of the stl file.imbue(std::locale()); file << std::put_time(&tm, "%d/%m/%y-%H:%M:%S: "); #endif //__ANDROID__ file << outstring << std::endl; file.close(); } else { log(message, type, Output::Console); log("Above message was intended for log file. Opening file probably failed.", Type::Warning, Output::Console); } } } static const std::string& bufferString(){ return stringOutput(); } private: static std::list& buffer(){ static std::list buffer; return buffer; } static std::string& stringOutput() { static std::string output; return output; } static void updateOutString(std::size_t maxBuffer) { static size_t count = 0; stringOutput().append(buffer().back()); stringOutput().append("\n"); count++; if (count > maxBuffer) { stringOutput() = stringOutput().substr(stringOutput().find_first_of('\n') + 1, stringOutput().size()); count--; } } }; } #ifndef _DEBUG_ #define LOG(message, type) #else #define LOG(message, type) {\ std::stringstream ss; \ ss << message << " (" << __FILE__ << ", " << __LINE__ << ")"; \ tmx::Logger::log(ss.str(), type);} #endif //_DEBUG_ #endif //TMXLITE_LOGGER_HPP_ ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/detail/pugiconfig.hpp ================================================ /** * pugixml parser - version 1.7 * -------------------------------------------------------- * Copyright (C) 2006-2015, 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 enable compact mode // #define PUGIXML_COMPACT // Uncomment this to disable XPath // #define PUGIXML_NO_XPATH #ifdef __ANDROID__ // Uncomment this to disable STL #define PUGIXML_NO_STL // Uncomment this to disable exceptions #define PUGIXML_NO_EXCEPTIONS #endif //__ANDROID__ // Set this to control attributes for public classes/functions, i.e.: // #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL // #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL // #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall // In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead // Tune these constants to adjust memory-related behavior // #define PUGIXML_MEMORY_PAGE_SIZE 32768 // #define PUGIXML_MEMORY_OUTPUT_STACK 10240 // #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096 // Uncomment this to switch to header-only version //#define PUGIXML_HEADER_ONLY // Uncomment this to enable long long support // #define PUGIXML_HAS_LONG_LONG #endif /** * Copyright (c) 2006-2015 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. */ ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/detail/pugixml.LICENSE ================================================ /** * pugixml parser - version 1.7 * -------------------------------------------------------- * Copyright (C) 2006-2015, 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. * * This work is based on the pugxml parser, which is: * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) * * * * 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. */ ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/detail/pugixml.cpp ================================================ /** * pugixml parser - version 1.7 * -------------------------------------------------------- * Copyright (C) 2006-2015, 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 SOURCE_PUGIXML_CPP #define SOURCE_PUGIXML_CPP #include "pugixml.hpp" #include "Android.hpp" #include #include #include #include #include // Fix for mingw, even if it should be in limits.h #ifndef LLONG_MIN #define LLONG_MIN (-9223372036854775807LL - 1) #define LLONG_MAX 9223372036854775807LL #define ULLONG_MAX 18446744073709551615ULL #endif #ifdef PUGIXML_WCHAR_MODE # include #endif #ifndef PUGIXML_NO_XPATH # include # include # ifdef PUGIXML_NO_EXCEPTIONS # include # endif #endif #ifndef PUGIXML_NO_STL # include # include # include #endif // For placement new #include #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 4127) // conditional expression is constant # pragma warning(disable: 4324) // structure was padded due to __declspec(align()) # pragma warning(disable: 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 # pragma warning(disable: 4793) // function compiled as native: presence of '_setjmp' makes a function unmanaged #endif #ifdef __INTEL_COMPILER # pragma warning(disable: 177) // function was declared but never referenced # pragma warning(disable: 279) // controlling expression is constant # pragma warning(disable: 1478 1786) // function was declared "deprecated" # pragma warning(disable: 1684) // conversion from pointer to same-sized integral type #endif #if defined(__BORLANDC__) && defined(PUGIXML_HEADER_ONLY) # pragma warn -8080 // symbol is declared but never used; disabling this inside push/pop bracket does not make the warning go away #endif #ifdef __BORLANDC__ # pragma option push # pragma warn -8008 // condition is always false # pragma warn -8066 // unreachable code #endif #ifdef __SNC__ // Using diag_push/diag_pop does not disable the warnings inside templates due to a compiler bug # pragma diag_suppress=178 // function was declared but never referenced # pragma diag_suppress=237 // controlling expression is constant #endif // Inlining controls #if defined(_MSC_VER) && _MSC_VER >= 1300 # define PUGI__NO_INLINE __declspec(noinline) #elif defined(__GNUC__) # define PUGI__NO_INLINE __attribute__((noinline)) #else # define PUGI__NO_INLINE #endif // Branch weight controls #if defined(__GNUC__) # define PUGI__UNLIKELY(cond) __builtin_expect(cond, 0) #else # define PUGI__UNLIKELY(cond) (cond) #endif // Simple static assertion #define PUGI__STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed[0]; } // Digital Mars C++ bug workaround for passing char loaded from memory via stack #ifdef __DMC__ # define PUGI__DMC_VOLATILE volatile #else # define PUGI__DMC_VOLATILE #endif // Borland C++ bug workaround for not defining ::memcpy depending on header include order (can't always use std::memcpy because some compilers don't have it at all) #if defined(__BORLANDC__) && !defined(__MEM_H_USING_LIST) using std::memcpy; using std::memmove; #endif // In some environments MSVC is a compiler but the CRT lacks certain MSVC-specific features #if defined(_MSC_VER) && !defined(__S3E__) # define PUGI__MSVC_CRT_VERSION _MSC_VER #endif #ifdef PUGIXML_HEADER_ONLY # define PUGI__NS_BEGIN namespace pugi { namespace impl { # define PUGI__NS_END } } # define PUGI__FN inline # define PUGI__FN_NO_INLINE inline #else # if defined(_MSC_VER) && _MSC_VER < 1300 // MSVC6 seems to have an amusing bug with anonymous namespaces inside namespaces # define PUGI__NS_BEGIN namespace pugi { namespace impl { # define PUGI__NS_END } } # else # define PUGI__NS_BEGIN namespace pugi { namespace impl { namespace { # define PUGI__NS_END } } } # endif # define PUGI__FN # define PUGI__FN_NO_INLINE PUGI__NO_INLINE #endif // uintptr_t #if !defined(_MSC_VER) || _MSC_VER >= 1600 # include #else namespace pugi { # ifndef _UINTPTR_T_DEFINED typedef size_t uintptr_t; # endif typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; } #endif // Memory allocation PUGI__NS_BEGIN PUGI__FN void* default_allocate(size_t size) { return malloc(size); } PUGI__FN void default_deallocate(void* ptr) { free(ptr); } template struct xml_memory_management_function_storage { static allocation_function allocate; static deallocation_function deallocate; }; // Global allocation functions are stored in class statics so that in header mode linker deduplicates them // Without a template<> we'll get multiple definitions of the same static template allocation_function xml_memory_management_function_storage::allocate = default_allocate; template deallocation_function xml_memory_management_function_storage::deallocate = default_deallocate; typedef xml_memory_management_function_storage xml_memory; PUGI__NS_END // String utilities PUGI__NS_BEGIN // Get string length PUGI__FN size_t strlength(const char_t* s) { assert(s); #ifdef PUGIXML_WCHAR_MODE return wcslen(s); #else return strlen(s); #endif } // Compare two strings PUGI__FN bool strequal(const char_t* src, const char_t* dst) { assert(src && dst); #ifdef PUGIXML_WCHAR_MODE return wcscmp(src, dst) == 0; #else return strcmp(src, dst) == 0; #endif } // Compare lhs with [rhs_begin, rhs_end) PUGI__FN bool strequalrange(const char_t* lhs, const char_t* rhs, size_t count) { for (size_t i = 0; i < count; ++i) if (lhs[i] != rhs[i]) return false; return lhs[count] == 0; } // Get length of wide string, even if CRT lacks wide character support PUGI__FN size_t strlength_wide(const wchar_t* s) { assert(s); #ifdef PUGIXML_WCHAR_MODE return wcslen(s); #else const wchar_t* end = s; while (*end) end++; return static_cast(end - s); #endif } PUGI__NS_END // auto_ptr-like object for exception recovery PUGI__NS_BEGIN template struct auto_deleter { T* data; D deleter; auto_deleter(T* data_, D deleter_): data(data_), deleter(deleter_) { } ~auto_deleter() { if (data) deleter(data); } T* release() { T* result = data; data = 0; return result; } }; PUGI__NS_END #ifdef PUGIXML_COMPACT PUGI__NS_BEGIN class compact_hash_table { public: compact_hash_table(): _items(0), _capacity(0), _count(0) { } void clear() { if (_items) { xml_memory::deallocate(_items); _items = 0; _capacity = 0; _count = 0; } } void** find(const void* key) { assert(key); if (_capacity == 0) return 0; size_t hashmod = _capacity - 1; size_t bucket = hash(key) & hashmod; for (size_t probe = 0; probe <= hashmod; ++probe) { item_t& probe_item = _items[bucket]; if (probe_item.key == key) return &probe_item.value; if (probe_item.key == 0) return 0; // hash collision, quadratic probing bucket = (bucket + probe + 1) & hashmod; } assert(!"Hash table is full"); return 0; } void** insert(const void* key) { assert(key); assert(_count < _capacity * 3 / 4); size_t hashmod = _capacity - 1; size_t bucket = hash(key) & hashmod; for (size_t probe = 0; probe <= hashmod; ++probe) { item_t& probe_item = _items[bucket]; if (probe_item.key == 0) { probe_item.key = key; _count++; return &probe_item.value; } if (probe_item.key == key) return &probe_item.value; // hash collision, quadratic probing bucket = (bucket + probe + 1) & hashmod; } assert(!"Hash table is full"); return 0; } bool reserve() { if (_count + 16 >= _capacity - _capacity / 4) return rehash(); return true; } private: struct item_t { const void* key; void* value; }; item_t* _items; size_t _capacity; size_t _count; bool rehash(); static unsigned int hash(const void* key) { unsigned int h = static_cast(reinterpret_cast(key)); // MurmurHash3 32-bit finalizer h ^= h >> 16; h *= 0x85ebca6bu; h ^= h >> 13; h *= 0xc2b2ae35u; h ^= h >> 16; return h; } }; PUGI__FN_NO_INLINE bool compact_hash_table::rehash() { compact_hash_table rt; rt._capacity = (_capacity == 0) ? 32 : _capacity * 2; rt._items = static_cast(xml_memory::allocate(sizeof(item_t) * rt._capacity)); if (!rt._items) return false; memset(rt._items, 0, sizeof(item_t) * rt._capacity); for (size_t i = 0; i < _capacity; ++i) if (_items[i].key) *rt.insert(_items[i].key) = _items[i].value; if (_items) xml_memory::deallocate(_items); _capacity = rt._capacity; _items = rt._items; return true; } PUGI__NS_END #endif PUGI__NS_BEGIN static const size_t xml_memory_page_size = #ifdef PUGIXML_MEMORY_PAGE_SIZE PUGIXML_MEMORY_PAGE_SIZE #else 32768 #endif ; #ifdef PUGIXML_COMPACT static const uintptr_t xml_memory_block_alignment = 4; static const uintptr_t xml_memory_page_alignment = sizeof(void*); #else static const uintptr_t xml_memory_block_alignment = sizeof(void*); static const uintptr_t xml_memory_page_alignment = 64; static const uintptr_t xml_memory_page_pointer_mask = ~(xml_memory_page_alignment - 1); #endif // extra metadata bits static const uintptr_t xml_memory_page_contents_shared_mask = 32; 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; // combined masks for string uniqueness static const uintptr_t xml_memory_page_name_allocated_or_shared_mask = xml_memory_page_name_allocated_mask | xml_memory_page_contents_shared_mask; static const uintptr_t xml_memory_page_value_allocated_or_shared_mask = xml_memory_page_value_allocated_mask | xml_memory_page_contents_shared_mask; #ifdef PUGIXML_COMPACT #define PUGI__GETPAGE_IMPL(header) (header).get_page() #else #define PUGI__GETPAGE_IMPL(header) reinterpret_cast((header) & impl::xml_memory_page_pointer_mask) #endif #define PUGI__GETPAGE(n) PUGI__GETPAGE_IMPL((n)->header) #define PUGI__NODETYPE(n) static_cast(((n)->header & impl::xml_memory_page_type_mask) + 1) struct xml_allocator; struct xml_memory_page { static xml_memory_page* construct(void* memory) { xml_memory_page* result = static_cast(memory); result->allocator = 0; result->prev = 0; result->next = 0; result->busy_size = 0; result->freed_size = 0; #ifdef PUGIXML_COMPACT result->compact_string_base = 0; result->compact_shared_parent = 0; result->compact_page_marker = 0; #endif return result; } xml_allocator* allocator; xml_memory_page* prev; xml_memory_page* next; size_t busy_size; size_t freed_size; #ifdef PUGIXML_COMPACT char_t* compact_string_base; void* compact_shared_parent; uint32_t* compact_page_marker; #endif }; struct xml_memory_string_header { uint16_t page_offset; // offset from page->data uint16_t full_size; // 0 if string occupies whole page }; struct xml_allocator { xml_allocator(xml_memory_page* root): _root(root), _busy_size(root->busy_size) { #ifdef PUGIXML_COMPACT _hash = 0; #endif } xml_memory_page* allocate_page(size_t data_size) { size_t size = sizeof(xml_memory_page) + data_size; // allocate block with some alignment, leaving memory for worst-case padding void* memory = xml_memory::allocate(size + xml_memory_page_alignment); if (!memory) return 0; // align to next page boundary (note: this guarantees at least 1 usable byte before the page) char* page_memory = reinterpret_cast((reinterpret_cast(memory) + xml_memory_page_alignment) & ~(xml_memory_page_alignment - 1)); // prepare page structure xml_memory_page* page = xml_memory_page::construct(page_memory); assert(page); page->allocator = _root->allocator; // record the offset for freeing the memory block assert(page_memory > memory && page_memory - static_cast(memory) <= 127); page_memory[-1] = static_cast(page_memory - static_cast(memory)); return page; } static void deallocate_page(xml_memory_page* page) { char* page_memory = reinterpret_cast(page); xml_memory::deallocate(page_memory - page_memory[-1]); } void* allocate_memory_oob(size_t size, xml_memory_page*& out_page); void* allocate_memory(size_t size, xml_memory_page*& out_page) { if (PUGI__UNLIKELY(_busy_size + size > xml_memory_page_size)) return allocate_memory_oob(size, out_page); void* buf = reinterpret_cast(_root) + sizeof(xml_memory_page) + _busy_size; _busy_size += size; out_page = _root; return buf; } #ifdef PUGIXML_COMPACT void* allocate_object(size_t size, xml_memory_page*& out_page) { void* result = allocate_memory(size + sizeof(uint32_t), out_page); if (!result) return 0; // adjust for marker ptrdiff_t offset = static_cast(result) - reinterpret_cast(out_page->compact_page_marker); if (PUGI__UNLIKELY(static_cast(offset) >= 256 * xml_memory_block_alignment)) { // insert new marker uint32_t* marker = static_cast(result); *marker = static_cast(reinterpret_cast(marker) - reinterpret_cast(out_page)); out_page->compact_page_marker = marker; // since we don't reuse the page space until we reallocate it, we can just pretend that we freed the marker block // this will make sure deallocate_memory correctly tracks the size out_page->freed_size += sizeof(uint32_t); return marker + 1; } else { // roll back uint32_t part _busy_size -= sizeof(uint32_t); return result; } } #else void* allocate_object(size_t size, xml_memory_page*& out_page) { return allocate_memory(size, out_page); } #endif void deallocate_memory(void* ptr, size_t size, xml_memory_page* page) { if (page == _root) page->busy_size = _busy_size; assert(ptr >= reinterpret_cast(page) + sizeof(xml_memory_page) && ptr < reinterpret_cast(page) + sizeof(xml_memory_page) + page->busy_size); (void)!ptr; page->freed_size += size; assert(page->freed_size <= page->busy_size); if (page->freed_size == page->busy_size) { if (page->next == 0) { assert(_root == page); // top page freed, just reset sizes page->busy_size = 0; page->freed_size = 0; #ifdef PUGIXML_COMPACT // reset compact state to maximize efficiency page->compact_string_base = 0; page->compact_shared_parent = 0; page->compact_page_marker = 0; #endif _busy_size = 0; } else { assert(_root != page); assert(page->prev); // remove from the list page->prev->next = page->next; page->next->prev = page->prev; // deallocate deallocate_page(page); } } } char_t* allocate_string(size_t length) { static const size_t max_encoded_offset = (1 << 16) * xml_memory_block_alignment; PUGI__STATIC_ASSERT(xml_memory_page_size <= max_encoded_offset); // allocate memory for string and header block size_t size = sizeof(xml_memory_string_header) + length * sizeof(char_t); // round size up to block alignment boundary size_t full_size = (size + (xml_memory_block_alignment - 1)) & ~(xml_memory_block_alignment - 1); xml_memory_page* page; xml_memory_string_header* header = static_cast(allocate_memory(full_size, page)); if (!header) return 0; // setup header ptrdiff_t page_offset = reinterpret_cast(header) - reinterpret_cast(page) - sizeof(xml_memory_page); assert(page_offset % xml_memory_block_alignment == 0); assert(page_offset >= 0 && static_cast(page_offset) < max_encoded_offset); header->page_offset = static_cast(static_cast(page_offset) / xml_memory_block_alignment); // full_size == 0 for large strings that occupy the whole page assert(full_size % xml_memory_block_alignment == 0); assert(full_size < max_encoded_offset || (page->busy_size == full_size && page_offset == 0)); header->full_size = static_cast(full_size < max_encoded_offset ? full_size / xml_memory_block_alignment : 0); // round-trip through void* to avoid 'cast increases required alignment of target type' warning // header is guaranteed a pointer-sized alignment, which should be enough for char_t return static_cast(static_cast(header + 1)); } void deallocate_string(char_t* string) { // this function casts pointers through void* to avoid 'cast increases required alignment of target type' warnings // we're guaranteed the proper (pointer-sized) alignment on the input string if it was allocated via allocate_string // get header xml_memory_string_header* header = static_cast(static_cast(string)) - 1; assert(header); // deallocate size_t page_offset = sizeof(xml_memory_page) + header->page_offset * xml_memory_block_alignment; xml_memory_page* page = reinterpret_cast(static_cast(reinterpret_cast(header) - page_offset)); // if full_size == 0 then this string occupies the whole page size_t full_size = header->full_size == 0 ? page->busy_size : header->full_size * xml_memory_block_alignment; deallocate_memory(header, full_size, page); } bool reserve() { #ifdef PUGIXML_COMPACT return _hash->reserve(); #else return true; #endif } xml_memory_page* _root; size_t _busy_size; #ifdef PUGIXML_COMPACT compact_hash_table* _hash; #endif }; PUGI__FN_NO_INLINE void* xml_allocator::allocate_memory_oob(size_t size, xml_memory_page*& out_page) { const size_t large_allocation_threshold = xml_memory_page_size / 4; xml_memory_page* page = allocate_page(size <= large_allocation_threshold ? xml_memory_page_size : size); out_page = page; if (!page) return 0; if (size <= large_allocation_threshold) { _root->busy_size = _busy_size; // insert page at the end of linked list page->prev = _root; _root->next = page; _root = page; _busy_size = size; } else { // insert page before the end of linked list, so that it is deleted as soon as possible // the last page is not deleted even if it's empty (see deallocate_memory) assert(_root->prev); page->prev = _root->prev; page->next = _root; _root->prev->next = page; _root->prev = page; page->busy_size = size; } return reinterpret_cast(page) + sizeof(xml_memory_page); } PUGI__NS_END #ifdef PUGIXML_COMPACT PUGI__NS_BEGIN static const uintptr_t compact_alignment_log2 = 2; static const uintptr_t compact_alignment = 1 << compact_alignment_log2; class compact_header { public: compact_header(xml_memory_page* page, unsigned int flags) { PUGI__STATIC_ASSERT(xml_memory_block_alignment == compact_alignment); ptrdiff_t offset = (reinterpret_cast(this) - reinterpret_cast(page->compact_page_marker)); assert(offset % compact_alignment == 0 && static_cast(offset) < 256 * compact_alignment); _page = static_cast(offset >> compact_alignment_log2); _flags = static_cast(flags); } void operator&=(uintptr_t mod) { _flags &= mod; } void operator|=(uintptr_t mod) { _flags |= mod; } uintptr_t operator&(uintptr_t mod) const { return _flags & mod; } xml_memory_page* get_page() const { const char* page_marker = reinterpret_cast(this) - (_page << compact_alignment_log2); const char* page = page_marker - *reinterpret_cast(page_marker); return const_cast(reinterpret_cast(page)); } private: unsigned char _page; unsigned char _flags; }; PUGI__FN xml_memory_page* compact_get_page(const void* object, int header_offset) { const compact_header* header = reinterpret_cast(static_cast(object) - header_offset); return header->get_page(); } template PUGI__FN_NO_INLINE T* compact_get_value(const void* object) { return static_cast(*compact_get_page(object, header_offset)->allocator->_hash->find(object)); } template PUGI__FN_NO_INLINE void compact_set_value(const void* object, T* value) { *compact_get_page(object, header_offset)->allocator->_hash->insert(object) = value; } template class compact_pointer { public: compact_pointer(): _data(0) { } void operator=(const compact_pointer& rhs) { *this = rhs + 0; } void operator=(T* value) { if (value) { // value is guaranteed to be compact-aligned; 'this' is not // our decoding is based on 'this' aligned to compact alignment downwards (see operator T*) // so for negative offsets (e.g. -3) we need to adjust the diff by compact_alignment - 1 to // compensate for arithmetic shift rounding for negative values ptrdiff_t diff = reinterpret_cast(value) - reinterpret_cast(this); ptrdiff_t offset = ((diff + int(compact_alignment - 1)) >> compact_alignment_log2) - start; if (static_cast(offset) <= 253) _data = static_cast(offset + 1); else { compact_set_value(this, value); _data = 255; } } else _data = 0; } operator T*() const { if (_data) { if (_data < 255) { uintptr_t base = reinterpret_cast(this) & ~(compact_alignment - 1); return reinterpret_cast(base + ((_data - 1 + start) << compact_alignment_log2)); } else return compact_get_value(this); } else return 0; } T* operator->() const { return operator T*(); } private: unsigned char _data; }; template class compact_pointer_parent { public: compact_pointer_parent(): _data(0) { } void operator=(const compact_pointer_parent& rhs) { *this = rhs + 0; } void operator=(T* value) { if (value) { // value is guaranteed to be compact-aligned; 'this' is not // our decoding is based on 'this' aligned to compact alignment downwards (see operator T*) // so for negative offsets (e.g. -3) we need to adjust the diff by compact_alignment - 1 to // compensate for arithmetic shift behavior for negative values ptrdiff_t diff = reinterpret_cast(value) - reinterpret_cast(this); ptrdiff_t offset = ((diff + int(compact_alignment - 1)) >> compact_alignment_log2) + 65533; if (static_cast(offset) <= 65533) { _data = static_cast(offset + 1); } else { xml_memory_page* page = compact_get_page(this, header_offset); if (PUGI__UNLIKELY(page->compact_shared_parent == 0)) page->compact_shared_parent = value; if (page->compact_shared_parent == value) { _data = 65534; } else { compact_set_value(this, value); _data = 65535; } } } else { _data = 0; } } operator T*() const { if (_data) { if (_data < 65534) { uintptr_t base = reinterpret_cast(this) & ~(compact_alignment - 1); return reinterpret_cast(base + ((_data - 1 - 65533) << compact_alignment_log2)); } else if (_data == 65534) return static_cast(compact_get_page(this, header_offset)->compact_shared_parent); else return compact_get_value(this); } else return 0; } T* operator->() const { return operator T*(); } private: uint16_t _data; }; template class compact_string { public: compact_string(): _data(0) { } void operator=(const compact_string& rhs) { *this = rhs + 0; } void operator=(char_t* value) { if (value) { xml_memory_page* page = compact_get_page(this, header_offset); if (PUGI__UNLIKELY(page->compact_string_base == 0)) page->compact_string_base = value; ptrdiff_t offset = value - page->compact_string_base; if (static_cast(offset) < (65535 << 7)) { uint16_t* base = reinterpret_cast(reinterpret_cast(this) - base_offset); if (*base == 0) { *base = static_cast((offset >> 7) + 1); _data = static_cast((offset & 127) + 1); } else { ptrdiff_t remainder = offset - ((*base - 1) << 7); if (static_cast(remainder) <= 253) { _data = static_cast(remainder + 1); } else { compact_set_value(this, value); _data = 255; } } } else { compact_set_value(this, value); _data = 255; } } else { _data = 0; } } operator char_t*() const { if (_data) { if (_data < 255) { xml_memory_page* page = compact_get_page(this, header_offset); const uint16_t* base = reinterpret_cast(reinterpret_cast(this) - base_offset); assert(*base); ptrdiff_t offset = ((*base - 1) << 7) + (_data - 1); return page->compact_string_base + offset; } else { return compact_get_value(this); } } else return 0; } private: unsigned char _data; }; PUGI__NS_END #endif #ifdef PUGIXML_COMPACT namespace pugi { struct xml_attribute_struct { xml_attribute_struct(impl::xml_memory_page* page): header(page, 0), namevalue_base(0) { PUGI__STATIC_ASSERT(sizeof(xml_attribute_struct) == 8); } impl::compact_header header; uint16_t namevalue_base; impl::compact_string<4, 2> name; impl::compact_string<5, 3> value; impl::compact_pointer prev_attribute_c; impl::compact_pointer next_attribute; }; struct xml_node_struct { xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(page, type - 1), namevalue_base(0) { PUGI__STATIC_ASSERT(sizeof(xml_node_struct) == 12); } impl::compact_header header; uint16_t namevalue_base; impl::compact_string<4, 2> name; impl::compact_string<5, 3> value; impl::compact_pointer_parent parent; impl::compact_pointer first_child; impl::compact_pointer prev_sibling_c; impl::compact_pointer next_sibling; impl::compact_pointer first_attribute; }; } #else namespace pugi { struct xml_attribute_struct { xml_attribute_struct(impl::xml_memory_page* page): header(reinterpret_cast(page)), name(0), value(0), prev_attribute_c(0), next_attribute(0) { } uintptr_t header; char_t* name; char_t* value; xml_attribute_struct* prev_attribute_c; xml_attribute_struct* next_attribute; }; struct xml_node_struct { xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(reinterpret_cast(page) | (type - 1)), name(0), value(0), parent(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0) { } uintptr_t header; char_t* name; char_t* value; xml_node_struct* parent; xml_node_struct* first_child; xml_node_struct* prev_sibling_c; xml_node_struct* next_sibling; xml_attribute_struct* first_attribute; }; } #endif PUGI__NS_BEGIN struct xml_extra_buffer { char_t* buffer; xml_extra_buffer* next; }; struct xml_document_struct: public xml_node_struct, public xml_allocator { xml_document_struct(xml_memory_page* page): xml_node_struct(page, node_document), xml_allocator(page), buffer(0), extra_buffers(0) { #ifdef PUGIXML_COMPACT _hash = &hash; #endif } const char_t* buffer; xml_extra_buffer* extra_buffers; #ifdef PUGIXML_COMPACT compact_hash_table hash; #endif }; template inline xml_allocator& get_allocator(const Object* object) { assert(object); return *PUGI__GETPAGE(object)->allocator; } template inline xml_document_struct& get_document(const Object* object) { assert(object); return *static_cast(PUGI__GETPAGE(object)->allocator); } PUGI__NS_END // Low-level DOM operations PUGI__NS_BEGIN inline xml_attribute_struct* allocate_attribute(xml_allocator& alloc) { xml_memory_page* page; void* memory = alloc.allocate_object(sizeof(xml_attribute_struct), page); if (!memory) return 0; return new (memory) xml_attribute_struct(page); } inline xml_node_struct* allocate_node(xml_allocator& alloc, xml_node_type type) { xml_memory_page* page; void* memory = alloc.allocate_object(sizeof(xml_node_struct), page); if (!memory) return 0; return new (memory) xml_node_struct(page, type); } inline void destroy_attribute(xml_attribute_struct* a, xml_allocator& alloc) { if (a->header & impl::xml_memory_page_name_allocated_mask) alloc.deallocate_string(a->name); if (a->header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(a->value); alloc.deallocate_memory(a, sizeof(xml_attribute_struct), PUGI__GETPAGE(a)); } inline void destroy_node(xml_node_struct* n, xml_allocator& alloc) { if (n->header & impl::xml_memory_page_name_allocated_mask) alloc.deallocate_string(n->name); if (n->header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(n->value); for (xml_attribute_struct* attr = n->first_attribute; attr; ) { xml_attribute_struct* next = attr->next_attribute; destroy_attribute(attr, alloc); attr = next; } for (xml_node_struct* child = n->first_child; child; ) { xml_node_struct* next = child->next_sibling; destroy_node(child, alloc); child = next; } alloc.deallocate_memory(n, sizeof(xml_node_struct), PUGI__GETPAGE(n)); } inline void append_node(xml_node_struct* child, xml_node_struct* node) { child->parent = node; xml_node_struct* head = node->first_child; if (head) { xml_node_struct* tail = head->prev_sibling_c; tail->next_sibling = child; child->prev_sibling_c = tail; head->prev_sibling_c = child; } else { node->first_child = child; child->prev_sibling_c = child; } } inline void prepend_node(xml_node_struct* child, xml_node_struct* node) { child->parent = node; xml_node_struct* head = node->first_child; if (head) { child->prev_sibling_c = head->prev_sibling_c; head->prev_sibling_c = child; } else child->prev_sibling_c = child; child->next_sibling = head; node->first_child = child; } inline void insert_node_after(xml_node_struct* child, xml_node_struct* node) { xml_node_struct* parent = node->parent; child->parent = parent; if (node->next_sibling) node->next_sibling->prev_sibling_c = child; else parent->first_child->prev_sibling_c = child; child->next_sibling = node->next_sibling; child->prev_sibling_c = node; node->next_sibling = child; } inline void insert_node_before(xml_node_struct* child, xml_node_struct* node) { xml_node_struct* parent = node->parent; child->parent = parent; if (node->prev_sibling_c->next_sibling) node->prev_sibling_c->next_sibling = child; else parent->first_child = child; child->prev_sibling_c = node->prev_sibling_c; child->next_sibling = node; node->prev_sibling_c = child; } inline void remove_node(xml_node_struct* node) { xml_node_struct* parent = node->parent; if (node->next_sibling) node->next_sibling->prev_sibling_c = node->prev_sibling_c; else parent->first_child->prev_sibling_c = node->prev_sibling_c; if (node->prev_sibling_c->next_sibling) node->prev_sibling_c->next_sibling = node->next_sibling; else parent->first_child = node->next_sibling; node->parent = 0; node->prev_sibling_c = 0; node->next_sibling = 0; } inline void append_attribute(xml_attribute_struct* attr, xml_node_struct* node) { xml_attribute_struct* head = node->first_attribute; if (head) { xml_attribute_struct* tail = head->prev_attribute_c; tail->next_attribute = attr; attr->prev_attribute_c = tail; head->prev_attribute_c = attr; } else { node->first_attribute = attr; attr->prev_attribute_c = attr; } } inline void prepend_attribute(xml_attribute_struct* attr, xml_node_struct* node) { xml_attribute_struct* head = node->first_attribute; if (head) { attr->prev_attribute_c = head->prev_attribute_c; head->prev_attribute_c = attr; } else attr->prev_attribute_c = attr; attr->next_attribute = head; node->first_attribute = attr; } inline void insert_attribute_after(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node) { if (place->next_attribute) place->next_attribute->prev_attribute_c = attr; else node->first_attribute->prev_attribute_c = attr; attr->next_attribute = place->next_attribute; attr->prev_attribute_c = place; place->next_attribute = attr; } inline void insert_attribute_before(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node) { if (place->prev_attribute_c->next_attribute) place->prev_attribute_c->next_attribute = attr; else node->first_attribute = attr; attr->prev_attribute_c = place->prev_attribute_c; attr->next_attribute = place; place->prev_attribute_c = attr; } inline void remove_attribute(xml_attribute_struct* attr, xml_node_struct* node) { if (attr->next_attribute) attr->next_attribute->prev_attribute_c = attr->prev_attribute_c; else node->first_attribute->prev_attribute_c = attr->prev_attribute_c; if (attr->prev_attribute_c->next_attribute) attr->prev_attribute_c->next_attribute = attr->next_attribute; else node->first_attribute = attr->next_attribute; attr->prev_attribute_c = 0; attr->next_attribute = 0; } PUGI__FN_NO_INLINE xml_node_struct* append_new_node(xml_node_struct* node, xml_allocator& alloc, xml_node_type type = node_element) { if (!alloc.reserve()) return 0; xml_node_struct* child = allocate_node(alloc, type); if (!child) return 0; append_node(child, node); return child; } PUGI__FN_NO_INLINE xml_attribute_struct* append_new_attribute(xml_node_struct* node, xml_allocator& alloc) { if (!alloc.reserve()) return 0; xml_attribute_struct* attr = allocate_attribute(alloc); if (!attr) return 0; append_attribute(attr, node); return attr; } PUGI__NS_END // Helper classes for code generation PUGI__NS_BEGIN struct opt_false { enum { value = 0 }; }; struct opt_true { enum { value = 1 }; }; PUGI__NS_END // Unicode utilities PUGI__NS_BEGIN inline uint16_t endian_swap(uint16_t value) { return static_cast(((value & 0xff) << 8) | (value >> 8)); } inline uint32_t endian_swap(uint32_t value) { return ((value & 0xff) << 24) | ((value & 0xff00) << 8) | ((value & 0xff0000) >> 8) | (value >> 24); } struct utf8_counter { typedef size_t value_type; static value_type low(value_type result, uint32_t ch) { // U+0000..U+007F if (ch < 0x80) return result + 1; // U+0080..U+07FF else if (ch < 0x800) return result + 2; // U+0800..U+FFFF else return result + 3; } static value_type high(value_type result, uint32_t) { // U+10000..U+10FFFF return result + 4; } }; struct utf8_writer { typedef uint8_t* value_type; static value_type low(value_type result, uint32_t ch) { // U+0000..U+007F if (ch < 0x80) { *result = static_cast(ch); return result + 1; } // U+0080..U+07FF else if (ch < 0x800) { result[0] = static_cast(0xC0 | (ch >> 6)); result[1] = static_cast(0x80 | (ch & 0x3F)); return result + 2; } // U+0800..U+FFFF else { result[0] = static_cast(0xE0 | (ch >> 12)); result[1] = static_cast(0x80 | ((ch >> 6) & 0x3F)); result[2] = static_cast(0x80 | (ch & 0x3F)); return result + 3; } } static value_type high(value_type result, uint32_t ch) { // U+10000..U+10FFFF result[0] = static_cast(0xF0 | (ch >> 18)); result[1] = static_cast(0x80 | ((ch >> 12) & 0x3F)); result[2] = static_cast(0x80 | ((ch >> 6) & 0x3F)); result[3] = static_cast(0x80 | (ch & 0x3F)); return result + 4; } static value_type any(value_type result, uint32_t ch) { return (ch < 0x10000) ? low(result, ch) : high(result, ch); } }; struct utf16_counter { typedef size_t value_type; static value_type low(value_type result, uint32_t) { return result + 1; } static value_type high(value_type result, uint32_t) { return result + 2; } }; struct utf16_writer { typedef uint16_t* value_type; static value_type low(value_type result, uint32_t ch) { *result = static_cast(ch); return result + 1; } static value_type high(value_type result, uint32_t ch) { uint32_t msh = static_cast(ch - 0x10000) >> 10; uint32_t lsh = static_cast(ch - 0x10000) & 0x3ff; result[0] = static_cast(0xD800 + msh); result[1] = static_cast(0xDC00 + lsh); return result + 2; } static value_type any(value_type result, uint32_t ch) { return (ch < 0x10000) ? low(result, ch) : high(result, ch); } }; struct utf32_counter { typedef size_t value_type; static value_type low(value_type result, uint32_t) { return result + 1; } static value_type high(value_type result, uint32_t) { return result + 1; } }; struct utf32_writer { typedef uint32_t* value_type; static value_type low(value_type result, uint32_t ch) { *result = ch; return result + 1; } static value_type high(value_type result, uint32_t ch) { *result = ch; return result + 1; } static value_type any(value_type result, uint32_t ch) { *result = ch; return result + 1; } }; struct latin1_writer { typedef uint8_t* value_type; static value_type low(value_type result, uint32_t ch) { *result = static_cast(ch > 255 ? '?' : ch); return result + 1; } static value_type high(value_type result, uint32_t ch) { (void)ch; *result = '?'; return result + 1; } }; struct utf8_decoder { typedef uint8_t type; template static inline typename Traits::value_type process(const uint8_t* data, size_t size, typename Traits::value_type result, Traits) { const uint8_t utf8_byte_mask = 0x3f; while (size) { uint8_t lead = *data; // 0xxxxxxx -> U+0000..U+007F if (lead < 0x80) { result = Traits::low(result, lead); data += 1; size -= 1; // process aligned single-byte (ascii) blocks if ((reinterpret_cast(data) & 3) == 0) { // round-trip through void* to silence 'cast increases required alignment of target type' warnings while (size >= 4 && (*static_cast(static_cast(data)) & 0x80808080) == 0) { result = Traits::low(result, data[0]); result = Traits::low(result, data[1]); result = Traits::low(result, data[2]); result = Traits::low(result, data[3]); data += 4; size -= 4; } } } // 110xxxxx -> U+0080..U+07FF else if (static_cast(lead - 0xC0) < 0x20 && size >= 2 && (data[1] & 0xc0) == 0x80) { result = Traits::low(result, ((lead & ~0xC0) << 6) | (data[1] & utf8_byte_mask)); data += 2; size -= 2; } // 1110xxxx -> U+0800-U+FFFF else if (static_cast(lead - 0xE0) < 0x10 && size >= 3 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80) { result = Traits::low(result, ((lead & ~0xE0) << 12) | ((data[1] & utf8_byte_mask) << 6) | (data[2] & utf8_byte_mask)); data += 3; size -= 3; } // 11110xxx -> U+10000..U+10FFFF else if (static_cast(lead - 0xF0) < 0x08 && size >= 4 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80 && (data[3] & 0xc0) == 0x80) { result = Traits::high(result, ((lead & ~0xF0) << 18) | ((data[1] & utf8_byte_mask) << 12) | ((data[2] & utf8_byte_mask) << 6) | (data[3] & utf8_byte_mask)); data += 4; size -= 4; } // 10xxxxxx or 11111xxx -> invalid else { data += 1; size -= 1; } } return result; } }; template struct utf16_decoder { typedef uint16_t type; template static inline typename Traits::value_type process(const uint16_t* data, size_t size, typename Traits::value_type result, Traits) { while (size) { uint16_t lead = opt_swap::value ? endian_swap(*data) : *data; // U+0000..U+D7FF if (lead < 0xD800) { result = Traits::low(result, lead); data += 1; size -= 1; } // U+E000..U+FFFF else if (static_cast(lead - 0xE000) < 0x2000) { result = Traits::low(result, lead); data += 1; size -= 1; } // surrogate pair lead else if (static_cast(lead - 0xD800) < 0x400 && size >= 2) { uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1]; if (static_cast(next - 0xDC00) < 0x400) { result = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff)); data += 2; size -= 2; } else { data += 1; size -= 1; } } else { data += 1; size -= 1; } } return result; } }; template struct utf32_decoder { typedef uint32_t type; template static inline typename Traits::value_type process(const uint32_t* data, size_t size, typename Traits::value_type result, Traits) { while (size) { uint32_t lead = opt_swap::value ? endian_swap(*data) : *data; // U+0000..U+FFFF if (lead < 0x10000) { result = Traits::low(result, lead); data += 1; size -= 1; } // U+10000..U+10FFFF else { result = Traits::high(result, lead); data += 1; size -= 1; } } return result; } }; struct latin1_decoder { typedef uint8_t type; template static inline typename Traits::value_type process(const uint8_t* data, size_t size, typename Traits::value_type result, Traits) { while (size) { result = Traits::low(result, *data); data += 1; size -= 1; } return result; } }; template struct wchar_selector; template <> struct wchar_selector<2> { typedef uint16_t type; typedef utf16_counter counter; typedef utf16_writer writer; typedef utf16_decoder decoder; }; template <> struct wchar_selector<4> { typedef uint32_t type; typedef utf32_counter counter; typedef utf32_writer writer; typedef utf32_decoder decoder; }; typedef wchar_selector::counter wchar_counter; typedef wchar_selector::writer wchar_writer; struct wchar_decoder { typedef wchar_t type; template static inline typename Traits::value_type process(const wchar_t* data, size_t size, typename Traits::value_type result, Traits traits) { typedef wchar_selector::decoder decoder; return decoder::process(reinterpret_cast(data), size, result, traits); } }; #ifdef PUGIXML_WCHAR_MODE PUGI__FN void convert_wchar_endian_swap(wchar_t* result, const wchar_t* data, size_t length) { for (size_t i = 0; i < length; ++i) result[i] = static_cast(endian_swap(static_cast::type>(data[i]))); } #endif PUGI__NS_END PUGI__NS_BEGIN enum chartype_t { ct_parse_pcdata = 1, // \0, &, \r, < ct_parse_attr = 2, // \0, &, \r, ', " ct_parse_attr_ws = 4, // \0, &, \r, ', ", \n, tab ct_space = 8, // \r, \n, space, tab ct_parse_cdata = 16, // \0, ], >, \r ct_parse_comment = 32, // \0, -, >, \r ct_symbol = 64, // Any symbol > 127, a-z, A-Z, 0-9, _, :, -, . ct_start_symbol = 128 // Any symbol > 127, a-z, A-Z, _, : }; static const unsigned char chartype_table[256] = { 55, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 0, 0, 63, 0, 0, // 0-15 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31 8, 0, 6, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 96, 64, 0, // 32-47 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 192, 0, 1, 0, 48, 0, // 48-63 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 64-79 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 16, 0, 192, // 80-95 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 96-111 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 0, 0, 0, // 112-127 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 128+ 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192 }; enum chartypex_t { ctx_special_pcdata = 1, // Any symbol >= 0 and < 32 (except \t, \r, \n), &, <, > ctx_special_attr = 2, // Any symbol >= 0 and < 32 (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, _, -, . }; static 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 PUGI__IS_CHARTYPE_IMPL(c, ct, table) ((static_cast(c) < 128 ? table[static_cast(c)] : table[128]) & (ct)) #else #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) (table[static_cast(c)] & (ct)) #endif #define PUGI__IS_CHARTYPE(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartype_table) #define PUGI__IS_CHARTYPEX(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartypex_table) PUGI__FN bool is_little_endian() { unsigned int ui = 1; return *reinterpret_cast(&ui) == 1; } PUGI__FN xml_encoding get_wchar_encoding() { PUGI__STATIC_ASSERT(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4); if (sizeof(wchar_t) == 2) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be; else return is_little_endian() ? encoding_utf32_le : encoding_utf32_be; } PUGI__FN 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); PUGI__DMC_VOLATILE uint8_t d0 = data[0], d1 = data[1], d2 = data[2], d3 = data[3]; return guess_buffer_encoding(d0, d1, d2, d3); } PUGI__FN bool get_mutable_buffer(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) { size_t length = size / sizeof(char_t); if (is_mutable) { out_buffer = static_cast(const_cast(contents)); out_length = length; } else { char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!buffer) return false; if (contents) memcpy(buffer, contents, length * sizeof(char_t)); else assert(length == 0); buffer[length] = 0; out_buffer = buffer; out_length = length + 1; } return true; } #ifdef PUGIXML_WCHAR_MODE PUGI__FN bool need_endian_swap_utf(xml_encoding le, xml_encoding re) { return (le == encoding_utf16_be && re == encoding_utf16_le) || (le == encoding_utf16_le && re == encoding_utf16_be) || (le == encoding_utf32_be && re == encoding_utf32_le) || (le == encoding_utf32_le && re == encoding_utf32_be); } PUGI__FN bool convert_buffer_endian_swap(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) { const char_t* data = static_cast(contents); size_t length = size / sizeof(char_t); if (is_mutable) { char_t* buffer = const_cast(data); convert_wchar_endian_swap(buffer, data, length); out_buffer = buffer; out_length = length; } else { char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!buffer) return false; convert_wchar_endian_swap(buffer, data, length); buffer[length] = 0; out_buffer = buffer; out_length = length + 1; } return true; } template PUGI__FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D) { const typename D::type* data = static_cast(contents); size_t data_length = size / sizeof(typename D::type); // first pass: get length in wchar_t units size_t length = D::process(data, data_length, 0, wchar_counter()); // allocate buffer of suitable length char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!buffer) return false; // second pass: convert utf16 input to wchar_t wchar_writer::value_type obegin = reinterpret_cast(buffer); wchar_writer::value_type oend = D::process(data, data_length, obegin, wchar_writer()); assert(oend == obegin + length); *oend = 0; out_buffer = buffer; out_length = length + 1; return true; } PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable) { // get native encoding xml_encoding wchar_encoding = get_wchar_encoding(); // fast path: no conversion required if (encoding == wchar_encoding) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); // only endian-swapping is required if (need_endian_swap_utf(encoding, wchar_encoding)) return convert_buffer_endian_swap(out_buffer, out_length, contents, size, is_mutable); // source encoding is utf8 if (encoding == encoding_utf8) return convert_buffer_generic(out_buffer, out_length, contents, size, utf8_decoder()); // source encoding is utf16 if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) { xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; return (native_encoding == encoding) ? convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder()) : convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder()); } // source encoding is utf32 if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) { xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; return (native_encoding == encoding) ? convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder()) : convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder()); } // source encoding is latin1 if (encoding == encoding_latin1) return convert_buffer_generic(out_buffer, out_length, contents, size, latin1_decoder()); assert(!"Invalid encoding"); return false; } #else template PUGI__FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D) { const typename D::type* data = static_cast(contents); size_t data_length = size / sizeof(typename D::type); // first pass: get length in utf8 units size_t length = D::process(data, data_length, 0, utf8_counter()); // allocate buffer of suitable length char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!buffer) return false; // second pass: convert utf16 input to utf8 uint8_t* obegin = reinterpret_cast(buffer); uint8_t* oend = D::process(data, data_length, obegin, utf8_writer()); assert(oend == obegin + length); *oend = 0; out_buffer = buffer; out_length = length + 1; return true; } PUGI__FN size_t get_latin1_7bit_prefix_length(const uint8_t* data, size_t size) { for (size_t i = 0; i < size; ++i) if (data[i] > 127) return i; return size; } PUGI__FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) { const uint8_t* data = static_cast(contents); size_t data_length = size; // get size of prefix that does not need utf8 conversion size_t prefix_length = get_latin1_7bit_prefix_length(data, data_length); assert(prefix_length <= data_length); const uint8_t* postfix = data + prefix_length; size_t postfix_length = data_length - prefix_length; // if no conversion is needed, just return the original buffer if (postfix_length == 0) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); // first pass: get length in utf8 units size_t length = prefix_length + latin1_decoder::process(postfix, postfix_length, 0, utf8_counter()); // allocate buffer of suitable length char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!buffer) return false; // second pass: convert latin1 input to utf8 memcpy(buffer, data, prefix_length); uint8_t* obegin = reinterpret_cast(buffer); uint8_t* oend = latin1_decoder::process(postfix, postfix_length, obegin + prefix_length, utf8_writer()); assert(oend == obegin + length); *oend = 0; out_buffer = buffer; out_length = length + 1; return true; } PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable) { // fast path: no conversion required if (encoding == encoding_utf8) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); // source encoding is utf16 if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) { xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; return (native_encoding == encoding) ? convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder()) : convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder()); } // source encoding is utf32 if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) { xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; return (native_encoding == encoding) ? convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder()) : convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder()); } // source encoding is latin1 if (encoding == encoding_latin1) return convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable); assert(!"Invalid encoding"); return false; } #endif PUGI__FN size_t as_utf8_begin(const wchar_t* str, size_t length) { // get length in utf8 characters return wchar_decoder::process(str, length, 0, utf8_counter()); } PUGI__FN void as_utf8_end(char* buffer, size_t size, const wchar_t* str, size_t length) { // convert to utf8 uint8_t* begin = reinterpret_cast(buffer); uint8_t* end = wchar_decoder::process(str, length, begin, utf8_writer()); assert(begin + size == end); (void)!end; (void)!size; } #ifndef PUGIXML_NO_STL PUGI__FN std::string as_utf8_impl(const wchar_t* str, size_t length) { // first pass: get length in utf8 characters size_t size = as_utf8_begin(str, length); // allocate resulting string std::string result; result.resize(size); // second pass: convert to utf8 if (size > 0) as_utf8_end(&result[0], size, str, length); return result; } PUGI__FN std::basic_string as_wide_impl(const char* str, size_t size) { const uint8_t* data = reinterpret_cast(str); // first pass: get length in wchar_t units size_t length = utf8_decoder::process(data, size, 0, wchar_counter()); // allocate resulting string std::basic_string result; result.resize(length); // second pass: convert to wchar_t if (length > 0) { wchar_writer::value_type begin = reinterpret_cast(&result[0]); wchar_writer::value_type end = utf8_decoder::process(data, size, begin, wchar_writer()); assert(begin + length == end); (void)!end; } return result; } #endif template inline bool strcpy_insitu_allow(size_t length, const Header& header, uintptr_t header_mask, char_t* target) { // never reuse shared memory if (header & xml_memory_page_contents_shared_mask) return false; size_t target_length = strlength(target); // always reuse document buffer memory if possible if ((header & header_mask) == 0) return target_length >= length; // reuse heap memory if waste is not too great const size_t reuse_threshold = 32; return target_length >= length && (target_length < reuse_threshold || target_length - length < target_length / 2); } template PUGI__FN bool strcpy_insitu(String& dest, Header& header, uintptr_t header_mask, const char_t* source, size_t source_length) { if (source_length == 0) { // empty string and null pointer are equivalent, so just deallocate old memory xml_allocator* alloc = PUGI__GETPAGE_IMPL(header)->allocator; if (header & header_mask) alloc->deallocate_string(dest); // mark the string as not allocated dest = 0; header &= ~header_mask; return true; } else if (dest && strcpy_insitu_allow(source_length, header, header_mask, dest)) { // we can reuse old buffer, so just copy the new data (including zero terminator) memcpy(dest, source, source_length * sizeof(char_t)); dest[source_length] = 0; return true; } else { xml_allocator* alloc = PUGI__GETPAGE_IMPL(header)->allocator; if (!alloc->reserve()) return false; // allocate new buffer char_t* buf = alloc->allocate_string(source_length + 1); if (!buf) return false; // copy the string (including zero terminator) memcpy(buf, source, source_length * sizeof(char_t)); buf[source_length] = 0; // deallocate old buffer (*after* the above to protect against overlapping memory and/or allocation failures) if (header & header_mask) alloc->deallocate_string(dest); // the string is now allocated, so set the flag dest = buf; header |= header_mask; return true; } } struct gap { char_t* end; size_t size; gap(): end(0), size(0) { } // Push new gap, move s count bytes further (skipping the gap). // Collapse previous gap. void push(char_t*& s, size_t count) { if (end) // there was a gap already; collapse it { // Move [old_gap_end, new_gap_start) to [old_gap_start, ...) assert(s >= end); memmove(end - size, end, reinterpret_cast(s) - reinterpret_cast(end)); } s += count; // end of current gap // "merge" two gaps end = s; size += count; } // Collapse all gaps, return past-the-end pointer char_t* flush(char_t* s) { if (end) { // Move [old_gap_end, current_pos) to [old_gap_start, ...) assert(s >= end); memmove(end - size, end, reinterpret_cast(s) - reinterpret_cast(end)); return s - size; } else return s; } }; PUGI__FN char_t* strconv_escape(char_t* s, gap& g) { char_t* stre = s + 1; switch (*stre) { case '#': // &#... { unsigned int ucsc = 0; if (stre[1] == 'x') // &#x... (hex code) { stre += 2; char_t ch = *stre; if (ch == ';') return stre; for (;;) { if (static_cast(ch - '0') <= 9) ucsc = 16 * ucsc + (ch - '0'); else if (static_cast((ch | ' ') - 'a') <= 5) ucsc = 16 * ucsc + ((ch | ' ') - 'a' + 10); else if (ch == ';') break; else // cancel return stre; ch = *++stre; } ++stre; } else // &#... (dec code) { char_t ch = *++stre; if (ch == ';') return stre; for (;;) { if (static_cast(static_cast(ch) - '0') <= 9) ucsc = 10 * ucsc + (ch - '0'); else if (ch == ';') break; else // cancel return stre; ch = *++stre; } ++stre; } #ifdef PUGIXML_WCHAR_MODE s = reinterpret_cast(wchar_writer::any(reinterpret_cast(s), ucsc)); #else s = reinterpret_cast(utf8_writer::any(reinterpret_cast(s), ucsc)); #endif g.push(s, stre - s); return stre; } case 'a': // &a { ++stre; if (*stre == 'm') // &am { if (*++stre == 'p' && *++stre == ';') // & { *s++ = '&'; ++stre; g.push(s, stre - s); return stre; } } else if (*stre == 'p') // &ap { if (*++stre == 'o' && *++stre == 's' && *++stre == ';') // ' { *s++ = '\''; ++stre; g.push(s, stre - s); return stre; } } break; } case 'g': // &g { if (*++stre == 't' && *++stre == ';') // > { *s++ = '>'; ++stre; g.push(s, stre - s); return stre; } break; } case 'l': // &l { if (*++stre == 't' && *++stre == ';') // < { *s++ = '<'; ++stre; g.push(s, stre - s); return stre; } break; } case 'q': // &q { if (*++stre == 'u' && *++stre == 'o' && *++stre == 't' && *++stre == ';') // " { *s++ = '"'; ++stre; g.push(s, stre - s); return stre; } break; } default: break; } return stre; } // Parser utilities #define PUGI__ENDSWITH(c, e) ((c) == (e) || ((c) == 0 && endch == (e))) #define PUGI__SKIPWS() { while (PUGI__IS_CHARTYPE(*s, ct_space)) ++s; } #define PUGI__OPTSET(OPT) ( optmsk & (OPT) ) #define PUGI__PUSHNODE(TYPE) { cursor = append_new_node(cursor, alloc, TYPE); if (!cursor) PUGI__THROW_ERROR(status_out_of_memory, s); } #define PUGI__POPNODE() { cursor = cursor->parent; } #define PUGI__SCANFOR(X) { while (*s != 0 && !(X)) ++s; } #define PUGI__SCANWHILE(X) { while (X) ++s; } #define PUGI__SCANWHILE_UNROLL(X) { for (;;) { char_t ss = s[0]; if (PUGI__UNLIKELY(!(X))) { break; } ss = s[1]; if (PUGI__UNLIKELY(!(X))) { s += 1; break; } ss = s[2]; if (PUGI__UNLIKELY(!(X))) { s += 2; break; } ss = s[3]; if (PUGI__UNLIKELY(!(X))) { s += 3; break; } s += 4; } } #define PUGI__ENDSEG() { ch = *s; *s = 0; ++s; } #define PUGI__THROW_ERROR(err, m) return error_offset = m, error_status = err, static_cast(0) #define PUGI__CHECK_ERROR(err, m) { if (*s == 0) PUGI__THROW_ERROR(err, m); } PUGI__FN char_t* strconv_comment(char_t* s, char_t endch) { gap g; while (true) { PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_comment)); if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair { *s++ = '\n'; // replace first one with 0x0a if (*s == '\n') g.push(s, 1); } else if (s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>')) // comment ends here { *g.flush(s) = 0; return s + (s[2] == '>' ? 3 : 2); } else if (*s == 0) { return 0; } else ++s; } } PUGI__FN char_t* strconv_cdata(char_t* s, char_t endch) { gap g; while (true) { PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_cdata)); if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair { *s++ = '\n'; // replace first one with 0x0a if (*s == '\n') g.push(s, 1); } else if (s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')) // CDATA ends here { *g.flush(s) = 0; return s + 1; } else if (*s == 0) { return 0; } else ++s; } } typedef char_t* (*strconv_pcdata_t)(char_t*); template struct strconv_pcdata_impl { static char_t* parse(char_t* s) { gap g; char_t* begin = s; while (true) { PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_pcdata)); if (*s == '<') // PCDATA ends here { char_t* end = g.flush(s); if (opt_trim::value) while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space)) --end; *end = 0; return s + 1; } else if (opt_eol::value && *s == '\r') // Either a single 0x0d or 0x0d 0x0a pair { *s++ = '\n'; // replace first one with 0x0a if (*s == '\n') g.push(s, 1); } else if (opt_escape::value && *s == '&') { s = strconv_escape(s, g); } else if (*s == 0) { char_t* end = g.flush(s); if (opt_trim::value) while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space)) --end; *end = 0; return s; } else ++s; } } }; PUGI__FN strconv_pcdata_t get_strconv_pcdata(unsigned int optmask) { PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_trim_pcdata == 0x0800); switch (((optmask >> 4) & 3) | ((optmask >> 9) & 4)) // get bitmask for flags (eol escapes trim) { case 0: return strconv_pcdata_impl::parse; case 1: return strconv_pcdata_impl::parse; case 2: return strconv_pcdata_impl::parse; case 3: return strconv_pcdata_impl::parse; case 4: return strconv_pcdata_impl::parse; case 5: return strconv_pcdata_impl::parse; case 6: return strconv_pcdata_impl::parse; case 7: return strconv_pcdata_impl::parse; default: assert(false); return 0; // 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 (PUGI__IS_CHARTYPE(*s, ct_space)) { char_t* str = s; do ++str; while (PUGI__IS_CHARTYPE(*str, ct_space)); g.push(s, str - s); } while (true) { PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws | ct_space)); if (*s == end_quote) { char_t* str = g.flush(s); do *str-- = 0; while (PUGI__IS_CHARTYPE(*str, ct_space)); return s + 1; } else if (PUGI__IS_CHARTYPE(*s, ct_space)) { *s++ = ' '; if (PUGI__IS_CHARTYPE(*s, ct_space)) { char_t* str = s + 1; while (PUGI__IS_CHARTYPE(*str, ct_space)) ++str; g.push(s, str - s); } } else if (opt_escape::value && *s == '&') { s = strconv_escape(s, g); } else if (!*s) { return 0; } else ++s; } } static char_t* parse_wconv(char_t* s, char_t end_quote) { gap g; while (true) { PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws)); if (*s == end_quote) { *g.flush(s) = 0; return s + 1; } else if (PUGI__IS_CHARTYPE(*s, ct_space)) { if (*s == '\r') { *s++ = ' '; if (*s == '\n') g.push(s, 1); } else *s++ = ' '; } else if (opt_escape::value && *s == '&') { s = strconv_escape(s, g); } else if (!*s) { return 0; } else ++s; } } static char_t* parse_eol(char_t* s, char_t end_quote) { gap g; while (true) { PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr)); if (*s == end_quote) { *g.flush(s) = 0; return s + 1; } else if (*s == '\r') { *s++ = '\n'; if (*s == '\n') g.push(s, 1); } else if (opt_escape::value && *s == '&') { s = strconv_escape(s, g); } else if (!*s) { return 0; } else ++s; } } static char_t* parse_simple(char_t* s, char_t end_quote) { gap g; while (true) { PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr)); if (*s == end_quote) { *g.flush(s) = 0; return s + 1; } else if (opt_escape::value && *s == '&') { s = strconv_escape(s, g); } else if (!*s) { return 0; } else ++s; } } }; PUGI__FN strconv_attribute_t get_strconv_attribute(unsigned int optmask) { PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80); switch ((optmask >> 4) & 15) // get bitmask for flags (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: assert(false); 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; xml_allocator* alloc_state; char_t* error_offset; xml_parse_status error_status; xml_parser(xml_allocator* alloc_): alloc(*alloc_), alloc_state(alloc_), error_offset(0), error_status(status_ok) { } ~xml_parser() { *alloc_state = alloc; } // DOCTYPE consists of nested sections of the following possible types: // , , "...", '...' // // // First group can not contain nested groups // Second group can contain nested groups of the same type // Third group can contain all other groups char_t* parse_doctype_primitive(char_t* s) { if (*s == '"' || *s == '\'') { // quoted string char_t ch = *s++; PUGI__SCANFOR(*s == ch); if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); s++; } else if (s[0] == '<' && s[1] == '?') { // s += 2; PUGI__SCANFOR(s[0] == '?' && s[1] == '>'); // no need for ENDSWITH because ?> can't terminate proper doctype if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); s += 2; } else if (s[0] == '<' && s[1] == '!' && s[2] == '-' && s[3] == '-') { s += 4; PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && s[2] == '>'); // no need for ENDSWITH because --> can't terminate proper doctype if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); s += 3; } else PUGI__THROW_ERROR(status_bad_doctype, s); return s; } char_t* parse_doctype_ignore(char_t* s) { size_t depth = 0; assert(s[0] == '<' && s[1] == '!' && s[2] == '['); s += 3; while (*s) { if (s[0] == '<' && s[1] == '!' && s[2] == '[') { // nested ignore section s += 3; depth++; } else if (s[0] == ']' && s[1] == ']' && s[2] == '>') { // ignore section end s += 3; if (depth == 0) return s; depth--; } else s++; } PUGI__THROW_ERROR(status_bad_doctype, s); } char_t* parse_doctype_group(char_t* s, char_t endch) { size_t depth = 0; assert((s[0] == '<' || s[0] == 0) && s[1] == '!'); s += 2; while (*s) { if (s[0] == '<' && s[1] == '!' && s[2] != '-') { if (s[2] == '[') { // ignore s = parse_doctype_ignore(s); if (!s) return s; } else { // some control group s += 2; depth++; } } else if (s[0] == '<' || s[0] == '"' || s[0] == '\'') { // unknown tag (forbidden), or some primitive group s = parse_doctype_primitive(s); if (!s) return s; } else if (*s == '>') { if (depth == 0) return s; depth--; s++; } else s++; } if (depth != 0 || endch != '>') PUGI__THROW_ERROR(status_bad_doctype, s); return s; } char_t* parse_exclamation(char_t* s, xml_node_struct* cursor, unsigned int optmsk, char_t endch) { // parse node contents, starting with exclamation mark ++s; if (*s == '-') // 'value = s; // Save the offset. } if (PUGI__OPTSET(parse_eol) && PUGI__OPTSET(parse_comments)) { s = strconv_comment(s, endch); if (!s) PUGI__THROW_ERROR(status_bad_comment, cursor->value); } else { // Scan for terminating '-->'. PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>')); PUGI__CHECK_ERROR(status_bad_comment, s); if (PUGI__OPTSET(parse_comments)) *s = 0; // Zero-terminate this segment at the first terminating '-'. s += (s[2] == '>' ? 3 : 2); // Step over the '\0->'. } } else PUGI__THROW_ERROR(status_bad_comment, s); } else if (*s == '[') { // 'value = s; // Save the offset. if (PUGI__OPTSET(parse_eol)) { s = strconv_cdata(s, endch); if (!s) PUGI__THROW_ERROR(status_bad_cdata, cursor->value); } else { // Scan for terminating ']]>'. PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')); PUGI__CHECK_ERROR(status_bad_cdata, s); *s++ = 0; // Zero-terminate this segment. } } else // Flagged for discard, but we still have to scan for the terminator. { // Scan for terminating ']]>'. PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')); PUGI__CHECK_ERROR(status_bad_cdata, s); ++s; } s += (s[1] == '>' ? 2 : 1); // Step over the last ']>'. } else PUGI__THROW_ERROR(status_bad_cdata, s); } else if (s[0] == 'D' && s[1] == 'O' && s[2] == 'C' && s[3] == 'T' && s[4] == 'Y' && s[5] == 'P' && PUGI__ENDSWITH(s[6], 'E')) { s -= 2; if (cursor->parent) PUGI__THROW_ERROR(status_bad_doctype, s); char_t* mark = s + 9; s = parse_doctype_group(s, endch); if (!s) return s; assert((*s == 0 && endch == '>') || *s == '>'); if (*s) *s++ = 0; if (PUGI__OPTSET(parse_doctype)) { while (PUGI__IS_CHARTYPE(*mark, ct_space)) ++mark; PUGI__PUSHNODE(node_doctype); cursor->value = mark; } } else if (*s == 0 && endch == '-') PUGI__THROW_ERROR(status_bad_comment, s); else if (*s == 0 && endch == '[') PUGI__THROW_ERROR(status_bad_cdata, s); else PUGI__THROW_ERROR(status_unrecognized_tag, s); return s; } char_t* parse_question(char_t* s, xml_node_struct*& ref_cursor, unsigned int optmsk, char_t endch) { // load into registers xml_node_struct* cursor = ref_cursor; char_t ch = 0; // parse node contents, starting with question mark ++s; // read PI target char_t* target = s; if (!PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_pi, s); PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); PUGI__CHECK_ERROR(status_bad_pi, s); // determine node type; stricmp / strcasecmp is not portable bool declaration = (target[0] | ' ') == 'x' && (target[1] | ' ') == 'm' && (target[2] | ' ') == 'l' && target + 3 == s; if (declaration ? PUGI__OPTSET(parse_declaration) : PUGI__OPTSET(parse_pi)) { if (declaration) { // disallow non top-level declarations if (cursor->parent) PUGI__THROW_ERROR(status_bad_pi, s); PUGI__PUSHNODE(node_declaration); } else { PUGI__PUSHNODE(node_pi); } cursor->name = target; PUGI__ENDSEG(); // parse value/attributes if (ch == '?') { // empty node if (!PUGI__ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_pi, s); s += (*s == '>'); PUGI__POPNODE(); } else if (PUGI__IS_CHARTYPE(ch, ct_space)) { PUGI__SKIPWS(); // scan for tag end char_t* value = s; PUGI__SCANFOR(s[0] == '?' && PUGI__ENDSWITH(s[1], '>')); PUGI__CHECK_ERROR(status_bad_pi, s); if (declaration) { // replace ending ? with / so that 'element' terminates properly *s = '/'; // we exit from this function with cursor at node_declaration, which is a signal to parse() to go to LOC_ATTRIBUTES s = value; } else { // store value and step over > cursor->value = value; PUGI__POPNODE(); PUGI__ENDSEG(); s += (*s == '>'); } } else PUGI__THROW_ERROR(status_bad_pi, s); } else { // scan for tag end PUGI__SCANFOR(s[0] == '?' && PUGI__ENDSWITH(s[1], '>')); PUGI__CHECK_ERROR(status_bad_pi, s); s += (s[1] == '>' ? 2 : 1); } // store from registers ref_cursor = cursor; return s; } char_t* parse_tree(char_t* s, xml_node_struct* root, unsigned int optmsk, char_t endch) { strconv_attribute_t strconv_attribute = get_strconv_attribute(optmsk); strconv_pcdata_t strconv_pcdata = get_strconv_pcdata(optmsk); char_t ch = 0; xml_node_struct* cursor = root; char_t* mark = s; while (*s != 0) { if (*s == '<') { ++s; LOC_TAG: if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // '<#...' { PUGI__PUSHNODE(node_element); // Append a new node to the tree. cursor->name = s; PUGI__SCANWHILE_UNROLL(PUGI__IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator. PUGI__ENDSEG(); // Save char in 'ch', terminate & step over. if (ch == '>') { // end of tag } else if (PUGI__IS_CHARTYPE(ch, ct_space)) { LOC_ATTRIBUTES: while (true) { PUGI__SKIPWS(); // Eat any whitespace. if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // <... #... { xml_attribute_struct* a = append_new_attribute(cursor, alloc); // Make space for this attribute. if (!a) PUGI__THROW_ERROR(status_out_of_memory, s); a->name = s; // Save the offset. PUGI__SCANWHILE_UNROLL(PUGI__IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator. PUGI__ENDSEG(); // Save char in 'ch', terminate & step over. if (PUGI__IS_CHARTYPE(ch, ct_space)) { PUGI__SKIPWS(); // Eat any whitespace. ch = *s; ++s; } if (ch == '=') // '<... #=...' { PUGI__SKIPWS(); // Eat any whitespace. if (*s == '"' || *s == '\'') // '<... #="...' { ch = *s; // Save quote char to avoid breaking on "''" -or- '""'. ++s; // Step over the quote. a->value = s; // Save the offset. s = strconv_attribute(s, ch); if (!s) PUGI__THROW_ERROR(status_bad_attribute, a->value); // After this line the loop continues from the start; // Whitespaces, / and > are ok, symbols and EOF are wrong, // everything else will be detected if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_attribute, s); } else PUGI__THROW_ERROR(status_bad_attribute, s); } else PUGI__THROW_ERROR(status_bad_attribute, s); } else if (*s == '/') { ++s; if (*s == '>') { PUGI__POPNODE(); s++; break; } else if (*s == 0 && endch == '>') { PUGI__POPNODE(); break; } else PUGI__THROW_ERROR(status_bad_start_element, s); } else if (*s == '>') { ++s; break; } else if (*s == 0 && endch == '>') { break; } else PUGI__THROW_ERROR(status_bad_start_element, s); } // !!! } else if (ch == '/') // '<#.../' { if (!PUGI__ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_start_element, s); PUGI__POPNODE(); // Pop. s += (*s == '>'); } else if (ch == 0) { // we stepped over null terminator, backtrack & handle closing tag --s; if (endch != '>') PUGI__THROW_ERROR(status_bad_start_element, s); } else PUGI__THROW_ERROR(status_bad_start_element, s); } else if (*s == '/') { ++s; char_t* name = cursor->name; if (!name) PUGI__THROW_ERROR(status_end_element_mismatch, s); while (PUGI__IS_CHARTYPE(*s, ct_symbol)) { if (*s++ != *name++) PUGI__THROW_ERROR(status_end_element_mismatch, s); } if (*name) { if (*s == 0 && name[0] == endch && name[1] == 0) PUGI__THROW_ERROR(status_bad_end_element, s); else PUGI__THROW_ERROR(status_end_element_mismatch, s); } PUGI__POPNODE(); // Pop. PUGI__SKIPWS(); if (*s == 0) { if (endch != '>') PUGI__THROW_ERROR(status_bad_end_element, s); } else { if (*s != '>') PUGI__THROW_ERROR(status_bad_end_element, s); ++s; } } else if (*s == '?') // 'first_child) continue; } } if (!PUGI__OPTSET(parse_trim_pcdata)) s = mark; if (cursor->parent || PUGI__OPTSET(parse_fragment)) { PUGI__PUSHNODE(node_pcdata); // Append a new node on the tree. cursor->value = s; // Save the offset. s = strconv_pcdata(s); PUGI__POPNODE(); // Pop since this is a standalone. if (!*s) break; } else { PUGI__SCANFOR(*s == '<'); // '...<' if (!*s) break; ++s; } // We're after '<' goto LOC_TAG; } } // check that last tag is closed if (cursor != root) PUGI__THROW_ERROR(status_end_element_mismatch, s); return s; } #ifdef PUGIXML_WCHAR_MODE static char_t* parse_skip_bom(char_t* s) { unsigned int bom = 0xfeff; return (s[0] == static_cast(bom)) ? s + 1 : s; } #else static char_t* parse_skip_bom(char_t* s) { return (s[0] == '\xef' && s[1] == '\xbb' && s[2] == '\xbf') ? s + 3 : s; } #endif static bool has_element_node_siblings(xml_node_struct* node) { while (node) { if (PUGI__NODETYPE(node) == node_element) return true; node = node->next_sibling; } return false; } static xml_parse_result parse(char_t* buffer, size_t length, xml_document_struct* xmldoc, xml_node_struct* root, unsigned int optmsk) { // early-out for empty documents if (length == 0) return make_parse_result(PUGI__OPTSET(parse_fragment) ? status_ok : status_no_document_element); // get last child of the root before parsing xml_node_struct* last_root_child = root->first_child ? root->first_child->prev_sibling_c + 0 : 0; // create parser on stack xml_parser parser(static_cast(xmldoc)); // save last character and make buffer zero-terminated (speeds up parsing) char_t endch = buffer[length - 1]; buffer[length - 1] = 0; // skip BOM to make sure it does not end up as part of parse output char_t* buffer_data = parse_skip_bom(buffer); // perform actual parsing parser.parse_tree(buffer_data, root, optmsk, endch); xml_parse_result result = make_parse_result(parser.error_status, parser.error_offset ? parser.error_offset - buffer : 0); assert(result.offset >= 0 && static_cast(result.offset) <= length); if (result) { // since we removed last character, we have to handle the only possible false positive (stray <) if (endch == '<') return make_parse_result(status_unrecognized_tag, length - 1); // check if there are any element nodes parsed xml_node_struct* first_root_child_parsed = last_root_child ? last_root_child->next_sibling + 0 : root->first_child; if (!PUGI__OPTSET(parse_fragment) && !has_element_node_siblings(first_root_child_parsed)) return make_parse_result(status_no_document_element, length - 1); } else { // roll back offset if it occurs on a null terminator in the source buffer if (result.offset > 0 && static_cast(result.offset) == length - 1 && endch == 0) result.offset--; } return result; } }; // Output facilities PUGI__FN xml_encoding get_write_native_encoding() { #ifdef PUGIXML_WCHAR_MODE return get_wchar_encoding(); #else return encoding_utf8; #endif } PUGI__FN xml_encoding get_write_encoding(xml_encoding encoding) { // replace wchar encoding with utf implementation if (encoding == encoding_wchar) return get_wchar_encoding(); // replace utf16 encoding with utf16 with specific endianness if (encoding == encoding_utf16) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be; // replace utf32 encoding with utf32 with specific endianness if (encoding == encoding_utf32) return is_little_endian() ? encoding_utf32_le : encoding_utf32_be; // only do autodetection if no explicit encoding is requested if (encoding != encoding_auto) return encoding; // assume utf8 encoding return encoding_utf8; } template PUGI__FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T) { PUGI__STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type)); typename T::value_type end = D::process(reinterpret_cast(data), length, dest, T()); return static_cast(end - dest) * sizeof(*dest); } template PUGI__FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T, bool opt_swap) { PUGI__STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type)); typename T::value_type end = D::process(reinterpret_cast(data), length, dest, T()); if (opt_swap) { for (typename T::value_type i = dest; i != end; ++i) *i = endian_swap(*i); } return static_cast(end - dest) * sizeof(*dest); } #ifdef PUGIXML_WCHAR_MODE PUGI__FN size_t get_valid_length(const char_t* data, size_t length) { if (length < 1) return 0; // discard last character if it's the lead of a surrogate pair return (sizeof(wchar_t) == 2 && static_cast(static_cast(data[length - 1]) - 0xD800) < 0x400) ? length - 1 : length; } PUGI__FN size_t convert_buffer_output(char_t* r_char, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding) { // only endian-swapping is required if (need_endian_swap_utf(encoding, get_wchar_encoding())) { convert_wchar_endian_swap(r_char, data, length); return length * sizeof(char_t); } // convert to utf8 if (encoding == encoding_utf8) return convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), utf8_writer()); // convert to utf16 if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) { xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; return convert_buffer_output_generic(r_u16, data, length, wchar_decoder(), utf16_writer(), native_encoding != encoding); } // convert to utf32 if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) { xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; return convert_buffer_output_generic(r_u32, data, length, wchar_decoder(), utf32_writer(), native_encoding != encoding); } // convert to latin1 if (encoding == encoding_latin1) return convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), latin1_writer()); assert(!"Invalid encoding"); return 0; } #else PUGI__FN size_t get_valid_length(const char_t* data, size_t length) { if (length < 5) return 0; for (size_t i = 1; i <= 4; ++i) { uint8_t ch = static_cast(data[length - i]); // either a standalone character or a leading one if ((ch & 0xc0) != 0x80) return length - i; } // there are four non-leading characters at the end, sequence tail is broken so might as well process the whole chunk return length; } PUGI__FN size_t convert_buffer_output(char_t* /* r_char */, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding) { if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) { xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; return convert_buffer_output_generic(r_u16, data, length, utf8_decoder(), utf16_writer(), native_encoding != encoding); } if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) { xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; return convert_buffer_output_generic(r_u32, data, length, utf8_decoder(), utf32_writer(), native_encoding != encoding); } if (encoding == encoding_latin1) return convert_buffer_output_generic(r_u8, data, length, utf8_decoder(), latin1_writer()); assert(!"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)) { PUGI__STATIC_ASSERT(bufcapacity >= 8); } size_t flush() { flush(buffer, bufsize); bufsize = 0; return 0; } void flush(const char_t* data, size_t size) { if (size == 0) return; // fast path, just write data if (encoding == get_write_native_encoding()) writer.write(data, size * sizeof(char_t)); else { // convert chunk size_t result = convert_buffer_output(scratch.data_char, scratch.data_u8, scratch.data_u16, scratch.data_u32, data, size, encoding); assert(result <= sizeof(scratch)); // write data writer.write(scratch.data_u8, result); } } void write_direct(const char_t* data, size_t length) { // flush the remaining buffer contents flush(); // handle large chunks if (length > bufcapacity) { if (encoding == get_write_native_encoding()) { // fast path, can just write data chunk writer.write(data, length * sizeof(char_t)); return; } // need to convert in suitable chunks while (length > bufcapacity) { // get chunk size by selecting such number of characters that are guaranteed to fit into scratch buffer // and form a complete codepoint sequence (i.e. discard start of last codepoint if necessary) size_t chunk_size = get_valid_length(data, bufcapacity); assert(chunk_size); // convert chunk and write flush(data, chunk_size); // iterate data += chunk_size; length -= chunk_size; } // small tail is copied below bufsize = 0; } memcpy(buffer + bufsize, data, length * sizeof(char_t)); bufsize += length; } void write_buffer(const char_t* data, size_t length) { size_t offset = bufsize; if (offset + length <= bufcapacity) { memcpy(buffer + offset, data, length * sizeof(char_t)); bufsize = offset + length; } else { write_direct(data, length); } } void write_string(const char_t* data) { // write the part of the string that fits in the buffer size_t offset = bufsize; while (*data && offset < bufcapacity) buffer[offset++] = *data++; // write the rest if (offset < bufcapacity) { bufsize = offset; } else { // backtrack a bit if we have split the codepoint size_t length = offset - bufsize; size_t extra = length - get_valid_length(data - length, length); bufsize = offset - extra; write_direct(data - extra, strlength(data) + extra); } } void write(char_t d0) { size_t offset = bufsize; if (offset > bufcapacity - 1) offset = flush(); buffer[offset + 0] = d0; bufsize = offset + 1; } void write(char_t d0, char_t d1) { size_t offset = bufsize; if (offset > bufcapacity - 2) offset = flush(); buffer[offset + 0] = d0; buffer[offset + 1] = d1; bufsize = offset + 2; } void write(char_t d0, char_t d1, char_t d2) { size_t offset = bufsize; if (offset > bufcapacity - 3) offset = flush(); buffer[offset + 0] = d0; buffer[offset + 1] = d1; buffer[offset + 2] = d2; bufsize = offset + 3; } void write(char_t d0, char_t d1, char_t d2, char_t d3) { size_t offset = bufsize; if (offset > bufcapacity - 4) offset = flush(); buffer[offset + 0] = d0; buffer[offset + 1] = d1; buffer[offset + 2] = d2; buffer[offset + 3] = d3; bufsize = offset + 4; } void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4) { size_t offset = bufsize; if (offset > bufcapacity - 5) offset = flush(); buffer[offset + 0] = d0; buffer[offset + 1] = d1; buffer[offset + 2] = d2; buffer[offset + 3] = d3; buffer[offset + 4] = d4; bufsize = offset + 5; } void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4, char_t d5) { size_t offset = bufsize; if (offset > bufcapacity - 6) offset = flush(); buffer[offset + 0] = d0; buffer[offset + 1] = d1; buffer[offset + 2] = d2; buffer[offset + 3] = d3; buffer[offset + 4] = d4; buffer[offset + 5] = d5; bufsize = offset + 6; } // utf8 maximum expansion: x4 (-> utf32) // utf16 maximum expansion: x2 (-> utf32) // utf32 maximum expansion: x1 enum { bufcapacitybytes = #ifdef PUGIXML_MEMORY_OUTPUT_STACK PUGIXML_MEMORY_OUTPUT_STACK #else 10240 #endif , bufcapacity = bufcapacitybytes / (sizeof(char_t) + 4) }; char_t buffer[bufcapacity]; union { uint8_t data_u8[4 * bufcapacity]; uint16_t data_u16[2 * bufcapacity]; uint32_t data_u32[bufcapacity]; char_t data_char[bufcapacity]; } scratch; xml_writer& writer; size_t bufsize; xml_encoding encoding; }; PUGI__FN void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type) { while (*s) { const char_t* prev = s; // While *s is a usual symbol PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPEX(ss, type)); writer.write_buffer(prev, static_cast(s - prev)); switch (*s) { case 0: break; case '&': writer.write('&', 'a', 'm', 'p', ';'); ++s; break; case '<': writer.write('&', 'l', 't', ';'); ++s; break; case '>': writer.write('&', 'g', 't', ';'); ++s; break; case '"': 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'), ';'); } } } } PUGI__FN void text_output(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags) { if (flags & format_no_escapes) writer.write_string(s); else text_output_escaped(writer, s, type); } PUGI__FN void text_output_cdata(xml_buffered_writer& writer, const char_t* s) { do { writer.write('<', '!', '[', 'C', 'D'); writer.write('A', 'T', 'A', '['); const char_t* prev = s; // look for ]]> sequence - we can't output it as is since it terminates CDATA while (*s && !(s[0] == ']' && s[1] == ']' && s[2] == '>')) ++s; // skip ]] if we stopped at ]]>, > will go to the next CDATA section if (*s) s += 2; writer.write_buffer(prev, static_cast(s - prev)); writer.write(']', ']', '>'); } while (*s); } PUGI__FN void text_output_indent(xml_buffered_writer& writer, const char_t* indent, size_t indent_length, unsigned int depth) { switch (indent_length) { case 1: { for (unsigned int i = 0; i < depth; ++i) writer.write(indent[0]); break; } case 2: { for (unsigned int i = 0; i < depth; ++i) writer.write(indent[0], indent[1]); break; } case 3: { for (unsigned int i = 0; i < depth; ++i) writer.write(indent[0], indent[1], indent[2]); break; } case 4: { for (unsigned int i = 0; i < depth; ++i) writer.write(indent[0], indent[1], indent[2], indent[3]); break; } default: { for (unsigned int i = 0; i < depth; ++i) writer.write_buffer(indent, indent_length); } } } PUGI__FN void node_output_comment(xml_buffered_writer& writer, const char_t* s) { writer.write('<', '!', '-', '-'); while (*s) { const char_t* prev = s; // look for -\0 or -- sequence - we can't output it since -- is illegal in comment body while (*s && !(s[0] == '-' && (s[1] == '-' || s[1] == 0))) ++s; writer.write_buffer(prev, static_cast(s - prev)); if (*s) { assert(*s == '-'); writer.write('-', ' '); ++s; } } writer.write('-', '-', '>'); } PUGI__FN void node_output_pi_value(xml_buffered_writer& writer, const char_t* s) { while (*s) { const char_t* prev = s; // look for ?> sequence - we can't output it since ?> terminates PI while (*s && !(s[0] == '?' && s[1] == '>')) ++s; writer.write_buffer(prev, static_cast(s - prev)); if (*s) { assert(s[0] == '?' && s[1] == '>'); writer.write('?', ' ', '>'); s += 2; } } } PUGI__FN void node_output_attributes(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute) { if ((flags & (format_indent_attributes | format_raw)) == format_indent_attributes) { writer.write('\n'); text_output_indent(writer, indent, indent_length, depth + 1); } else { writer.write(' '); } writer.write_string(a->name ? a->name : default_name); writer.write('=', '"'); if (a->value) text_output(writer, a->value, ctx_special_attr, flags); writer.write('"'); } } PUGI__FN bool node_output_start(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); const char_t* name = node->name ? node->name : default_name; writer.write('<'); writer.write_string(name); if (node->first_attribute) node_output_attributes(writer, node, indent, indent_length, flags, depth); if (!node->first_child) { writer.write(' ', '/', '>'); return false; } else { writer.write('>'); return true; } } PUGI__FN void node_output_end(xml_buffered_writer& writer, xml_node_struct* node) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); const char_t* name = node->name ? node->name : default_name; writer.write('<', '/'); writer.write_string(name); writer.write('>'); } PUGI__FN void node_output_simple(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); switch (PUGI__NODETYPE(node)) { case node_pcdata: text_output(writer, node->value ? node->value + 0 : PUGIXML_TEXT(""), ctx_special_pcdata, flags); break; case node_cdata: text_output_cdata(writer, node->value ? node->value + 0 : PUGIXML_TEXT("")); break; case node_comment: node_output_comment(writer, node->value ? node->value + 0 : PUGIXML_TEXT("")); break; case node_pi: writer.write('<', '?'); writer.write_string(node->name ? node->name : default_name); if (node->value) { writer.write(' '); node_output_pi_value(writer, node->value); } writer.write('?', '>'); break; case node_declaration: writer.write('<', '?'); writer.write_string(node->name ? node->name : default_name); node_output_attributes(writer, node, PUGIXML_TEXT(""), 0, flags | format_raw, 0); writer.write('?', '>'); break; case node_doctype: writer.write('<', '!', 'D', 'O', 'C'); writer.write('T', 'Y', 'P', 'E'); if (node->value) { writer.write(' '); writer.write_string(node->value); } writer.write('>'); break; default: assert(!"Invalid node type"); } } enum indent_flags_t { indent_newline = 1, indent_indent = 2 }; PUGI__FN void node_output(xml_buffered_writer& writer, xml_node_struct* root, const char_t* indent, unsigned int flags, unsigned int depth) { size_t indent_length = ((flags & (format_indent | format_indent_attributes)) && (flags & format_raw) == 0) ? strlength(indent) : 0; unsigned int indent_flags = indent_indent; xml_node_struct* node = root; do { assert(node); // begin writing current node if (PUGI__NODETYPE(node) == node_pcdata || PUGI__NODETYPE(node) == node_cdata) { node_output_simple(writer, node, flags); indent_flags = 0; } else { if ((indent_flags & indent_newline) && (flags & format_raw) == 0) writer.write('\n'); if ((indent_flags & indent_indent) && indent_length) text_output_indent(writer, indent, indent_length, depth); if (PUGI__NODETYPE(node) == node_element) { indent_flags = indent_newline | indent_indent; if (node_output_start(writer, node, indent, indent_length, flags, depth)) { node = node->first_child; depth++; continue; } } else if (PUGI__NODETYPE(node) == node_document) { indent_flags = indent_indent; if (node->first_child) { node = node->first_child; continue; } } else { node_output_simple(writer, node, flags); indent_flags = indent_newline | indent_indent; } } // continue to the next node while (node != root) { if (node->next_sibling) { node = node->next_sibling; break; } node = node->parent; // write closing node if (PUGI__NODETYPE(node) == node_element) { depth--; if ((indent_flags & indent_newline) && (flags & format_raw) == 0) writer.write('\n'); if ((indent_flags & indent_indent) && indent_length) text_output_indent(writer, indent, indent_length, depth); node_output_end(writer, node); indent_flags = indent_newline | indent_indent; } } } while (node != root); if ((indent_flags & indent_newline) && (flags & format_raw) == 0) writer.write('\n'); } PUGI__FN bool has_declaration(xml_node_struct* node) { for (xml_node_struct* child = node->first_child; child; child = child->next_sibling) { xml_node_type type = PUGI__NODETYPE(child); if (type == node_declaration) return true; if (type == node_element) return false; } return false; } PUGI__FN bool is_attribute_of(xml_attribute_struct* attr, xml_node_struct* node) { for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute) if (a == attr) return true; return false; } PUGI__FN bool allow_insert_attribute(xml_node_type parent) { return parent == node_element || parent == node_declaration; } PUGI__FN bool allow_insert_child(xml_node_type parent, xml_node_type child) { if (parent != node_document && parent != node_element) return false; if (child == node_document || child == node_null) return false; if (parent != node_document && (child == node_declaration || child == node_doctype)) return false; return true; } PUGI__FN bool allow_move(xml_node parent, xml_node child) { // check that child can be a child of parent if (!allow_insert_child(parent.type(), child.type())) return false; // check that node is not moved between documents if (parent.root() != child.root()) return false; // check that new parent is not in the child subtree xml_node cur = parent; while (cur) { if (cur == child) return false; cur = cur.parent(); } return true; } template PUGI__FN void node_copy_string(String& dest, Header& header, uintptr_t header_mask, char_t* source, Header& source_header, xml_allocator* alloc) { assert(!dest && (header & header_mask) == 0); if (source) { if (alloc && (source_header & header_mask) == 0) { dest = source; // since strcpy_insitu can reuse document buffer memory we need to mark both source and dest as shared header |= xml_memory_page_contents_shared_mask; source_header |= xml_memory_page_contents_shared_mask; } else strcpy_insitu(dest, header, header_mask, source, strlength(source)); } } PUGI__FN void node_copy_contents(xml_node_struct* dn, xml_node_struct* sn, xml_allocator* shared_alloc) { node_copy_string(dn->name, dn->header, xml_memory_page_name_allocated_mask, sn->name, sn->header, shared_alloc); node_copy_string(dn->value, dn->header, xml_memory_page_value_allocated_mask, sn->value, sn->header, shared_alloc); for (xml_attribute_struct* sa = sn->first_attribute; sa; sa = sa->next_attribute) { xml_attribute_struct* da = append_new_attribute(dn, get_allocator(dn)); if (da) { node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc); node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc); } } } PUGI__FN void node_copy_tree(xml_node_struct* dn, xml_node_struct* sn) { xml_allocator& alloc = get_allocator(dn); xml_allocator* shared_alloc = (&alloc == &get_allocator(sn)) ? &alloc : 0; node_copy_contents(dn, sn, shared_alloc); xml_node_struct* dit = dn; xml_node_struct* sit = sn->first_child; while (sit && sit != sn) { if (sit != dn) { xml_node_struct* copy = append_new_node(dit, alloc, PUGI__NODETYPE(sit)); if (copy) { node_copy_contents(copy, sit, shared_alloc); if (sit->first_child) { dit = copy; sit = sit->first_child; continue; } } } // continue to the next node do { if (sit->next_sibling) { sit = sit->next_sibling; break; } sit = sit->parent; dit = dit->parent; } while (sit != sn); } } PUGI__FN void node_copy_attribute(xml_attribute_struct* da, xml_attribute_struct* sa) { xml_allocator& alloc = get_allocator(da); xml_allocator* shared_alloc = (&alloc == &get_allocator(sa)) ? &alloc : 0; node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc); node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc); } inline bool is_text_node(xml_node_struct* node) { xml_node_type type = PUGI__NODETYPE(node); return type == node_pcdata || type == node_cdata; } // get value with conversion functions template U string_to_integer(const char_t* value, U minneg, U maxpos) { U result = 0; const char_t* s = value; while (PUGI__IS_CHARTYPE(*s, ct_space)) s++; bool negative = (*s == '-'); s += (*s == '+' || *s == '-'); bool overflow = false; if (s[0] == '0' && (s[1] | ' ') == 'x') { s += 2; const char_t* start = s; for (;;) { if (static_cast(*s - '0') < 10) result = result * 16 + (*s - '0'); else if (static_cast((*s | ' ') - 'a') < 6) result = result * 16 + ((*s | ' ') - 'a' + 10); else break; s++; } size_t digits = static_cast(s - start); overflow = digits > sizeof(U) * 2; } else { const char_t* start = s; for (;;) { if (static_cast(*s - '0') < 10) result = result * 10 + (*s - '0'); else break; s++; } size_t digits = static_cast(s - start); PUGI__STATIC_ASSERT(sizeof(U) == 8 || sizeof(U) == 4 || sizeof(U) == 2); const size_t max_digits10 = sizeof(U) == 8 ? 20 : sizeof(U) == 4 ? 10 : 5; const char max_lead = sizeof(U) == 8 ? '1' : sizeof(U) == 4 ? '4' : '6'; const size_t high_bit = sizeof(U) * 8 - 1; overflow = digits >= max_digits10 && !(digits == max_digits10 && (*start < max_lead || (*start == max_lead && result >> high_bit))); } if (negative) return (overflow || result > minneg) ? 0 - minneg : 0 - result; else return (overflow || result > maxpos) ? maxpos : result; } PUGI__FN int get_value_int(const char_t* value) { return string_to_integer(value, static_cast(INT_MIN), INT_MAX); } PUGI__FN unsigned int get_value_uint(const char_t* value) { return string_to_integer(value, 0, UINT_MAX); } PUGI__FN double get_value_double(const char_t* value) { #ifdef PUGIXML_WCHAR_MODE return wcstod(value, 0); #else return strtod(value, 0); #endif } PUGI__FN float get_value_float(const char_t* value) { #ifdef PUGIXML_WCHAR_MODE return static_cast(wcstod(value, 0)); #else return static_cast(strtod(value, 0)); #endif } PUGI__FN bool get_value_bool(const char_t* value) { // only look at first char char_t first = *value; // 1*, t* (true), T* (True), y* (yes), Y* (YES) return (first == '1' || first == 't' || first == 'T' || first == 'y' || first == 'Y'); } #ifdef PUGIXML_HAS_LONG_LONG PUGI__FN long long get_value_llong(const char_t* value) { return string_to_integer(value, static_cast(LLONG_MIN), LLONG_MAX); } PUGI__FN unsigned long long get_value_ullong(const char_t* value) { return string_to_integer(value, 0, ULLONG_MAX); } #endif template PUGI__FN char_t* integer_to_string(char_t* begin, char_t* end, U value, bool negative) { char_t* result = end - 1; U rest = negative ? 0 - value : value; do { *result-- = static_cast('0' + (rest % 10)); rest /= 10; } while (rest); assert(result >= begin); (void)begin; *result = '-'; return result + !negative; } // set value with conversion functions template PUGI__FN bool set_value_ascii(String& dest, Header& header, uintptr_t header_mask, char (&buf)[128]) { #ifdef PUGIXML_WCHAR_MODE char_t wbuf[128]; size_t offset = 0; for (; buf[offset]; ++offset) wbuf[offset] = buf[offset]; return strcpy_insitu(dest, header, header_mask, wbuf, offset); #else return strcpy_insitu(dest, header, header_mask, buf, strlength(buf)); #endif } template PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, int value) { char_t buf[64]; char_t* end = buf + sizeof(buf) / sizeof(buf[0]); char_t* begin = integer_to_string(buf, end, value, value < 0); return strcpy_insitu(dest, header, header_mask, begin, end - begin); } template PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, unsigned int value) { char_t buf[64]; char_t* end = buf + sizeof(buf) / sizeof(buf[0]); char_t* begin = integer_to_string(buf, end, value, false); return strcpy_insitu(dest, header, header_mask, begin, end - begin); } template PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, float value) { char buf[128]; sprintf(buf, "%.9g", value); return set_value_ascii(dest, header, header_mask, buf); } template PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, double value) { char buf[128]; sprintf(buf, "%.17g", value); return set_value_ascii(dest, header, header_mask, buf); } template PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, bool value) { return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"), value ? 4 : 5); } #ifdef PUGIXML_HAS_LONG_LONG template PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, long long value) { char_t buf[64]; char_t* end = buf + sizeof(buf) / sizeof(buf[0]); char_t* begin = integer_to_string(buf, end, value, value < 0); return strcpy_insitu(dest, header, header_mask, begin, end - begin); } template PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, unsigned long long value) { char_t buf[64]; char_t* end = buf + sizeof(buf) / sizeof(buf[0]); char_t* begin = integer_to_string(buf, end, value, false); return strcpy_insitu(dest, header, header_mask, begin, end - begin); } #endif PUGI__FN xml_parse_result load_buffer_impl(xml_document_struct* doc, xml_node_struct* root, void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own, char_t** out_buffer) { // check input buffer if (!contents && size) return make_parse_result(status_io_error); // get actual encoding xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size); // get private buffer char_t* buffer = 0; size_t length = 0; if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory); // delete original buffer if we performed a conversion if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents); // grab onto buffer if it's our buffer, user is responsible for deallocating contents himself if (own || buffer != contents) *out_buffer = buffer; // store buffer for offset_debug doc->buffer = buffer; // parse xml_parse_result res = impl::xml_parser::parse(buffer, length, doc, root, options); // remember encoding res.encoding = buffer_encoding; return res; } // we need to get length of entire file to load it in memory; the only (relatively) sane way to do it is via seek/tell trick PUGI__FN xml_parse_status get_file_size(FILE* file, size_t& out_result) { #if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE) // there are 64-bit versions of fseek/ftell, let's use them typedef __int64 length_type; _fseeki64(file, 0, SEEK_END); length_type length = _ftelli64(file); _fseeki64(file, 0, SEEK_SET); #elif defined(__MINGW32__) && !defined(__NO_MINGW_LFS) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR)) // there are 64-bit versions of fseek/ftell, let's use them typedef off64_t length_type; fseeko64(file, 0, SEEK_END); length_type length = ftello64(file); fseeko64(file, 0, SEEK_SET); #else // if this is a 32-bit OS, long is enough; if this is a unix system, long is 64-bit, which is enough; otherwise we can't do anything anyway. typedef long length_type; fseek(file, 0, SEEK_END); length_type length = ftell(file); fseek(file, 0, SEEK_SET); #endif // check for I/O errors if (length < 0) return status_io_error; // check for overflow size_t result = static_cast(length); if (static_cast(result) != length) return status_out_of_memory; // finalize out_result = result; return status_ok; } // This function assumes that buffer has extra sizeof(char_t) writable bytes after size PUGI__FN size_t zero_terminate_buffer(void* buffer, size_t size, xml_encoding encoding) { // We only need to zero-terminate if encoding conversion does not do it for us #ifdef PUGIXML_WCHAR_MODE xml_encoding wchar_encoding = get_wchar_encoding(); if (encoding == wchar_encoding || need_endian_swap_utf(encoding, wchar_encoding)) { size_t length = size / sizeof(char_t); static_cast(buffer)[length] = 0; return (length + 1) * sizeof(char_t); } #else if (encoding == encoding_utf8) { static_cast(buffer)[size] = 0; return size + 1; } #endif return size; } PUGI__FN xml_parse_result load_file_impl(xml_document_struct* doc, FILE* file, unsigned int options, xml_encoding encoding, char_t** out_buffer) { if (!file) return make_parse_result(status_file_not_found); // get file size (can result in I/O errors) size_t size = 0; xml_parse_status size_status = get_file_size(file, size); if (size_status != status_ok) return make_parse_result(size_status); size_t max_suffix_size = sizeof(char_t); // allocate buffer for the whole file char* contents = static_cast(xml_memory::allocate(size + max_suffix_size)); if (!contents) return make_parse_result(status_out_of_memory); // read file in memory size_t read_size = fread(contents, 1, size, file); if (read_size != size) { xml_memory::deallocate(contents); return make_parse_result(status_io_error); } xml_encoding real_encoding = get_buffer_encoding(encoding, contents, size); return load_buffer_impl(doc, doc, contents, zero_terminate_buffer(contents, size, real_encoding), options, real_encoding, true, true, out_buffer); } #ifndef PUGIXML_NO_STL template struct xml_stream_chunk { static xml_stream_chunk* create() { void* memory = xml_memory::allocate(sizeof(xml_stream_chunk)); if (!memory) return 0; return new (memory) xml_stream_chunk(); } static void destroy(xml_stream_chunk* chunk) { // free chunk chain while (chunk) { xml_stream_chunk* next_ = chunk->next; xml_memory::deallocate(chunk); chunk = next_; } } xml_stream_chunk(): next(0), size(0) { } xml_stream_chunk* next; size_t size; T data[xml_memory_page_size / sizeof(T)]; }; template PUGI__FN xml_parse_status load_stream_data_noseek(std::basic_istream& stream, void** out_buffer, size_t* out_size) { auto_deleter > chunks(0, xml_stream_chunk::destroy); // read file to a chunk list size_t total = 0; xml_stream_chunk* last = 0; while (!stream.eof()) { // allocate new chunk xml_stream_chunk* chunk = xml_stream_chunk::create(); if (!chunk) return status_out_of_memory; // append chunk to list if (last) last = last->next = chunk; else chunks.data = last = chunk; // read data to chunk stream.read(chunk->data, static_cast(sizeof(chunk->data) / sizeof(T))); chunk->size = static_cast(stream.gcount()) * sizeof(T); // read may set failbit | eofbit in case gcount() is less than read length, so check for other I/O errors if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error; // guard against huge files (chunk size is small enough to make this overflow check work) if (total + chunk->size < total) return status_out_of_memory; total += chunk->size; } size_t max_suffix_size = sizeof(char_t); // copy chunk list to a contiguous buffer char* buffer = static_cast(xml_memory::allocate(total + max_suffix_size)); if (!buffer) return status_out_of_memory; char* write = buffer; for (xml_stream_chunk* chunk = chunks.data; chunk; chunk = chunk->next) { assert(write + chunk->size <= buffer + total); memcpy(write, chunk->data, chunk->size); write += chunk->size; } assert(write == buffer + total); // return buffer *out_buffer = buffer; *out_size = total; return status_ok; } template PUGI__FN xml_parse_status load_stream_data_seek(std::basic_istream& stream, void** out_buffer, size_t* out_size) { // get length of remaining data in stream typename std::basic_istream::pos_type pos = stream.tellg(); stream.seekg(0, std::ios::end); std::streamoff length = stream.tellg() - pos; stream.seekg(pos); if (stream.fail() || pos < 0) return status_io_error; // guard against huge files size_t read_length = static_cast(length); if (static_cast(read_length) != length || length < 0) return status_out_of_memory; size_t max_suffix_size = sizeof(char_t); // read stream data into memory (guard against stream exceptions with buffer holder) auto_deleter buffer(xml_memory::allocate(read_length * sizeof(T) + max_suffix_size), xml_memory::deallocate); if (!buffer.data) return status_out_of_memory; stream.read(static_cast(buffer.data), static_cast(read_length)); // read may set failbit | eofbit in case gcount() is less than read_length (i.e. line ending conversion), so check for other I/O errors if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error; // return buffer size_t actual_length = static_cast(stream.gcount()); assert(actual_length <= read_length); *out_buffer = buffer.release(); *out_size = actual_length * sizeof(T); return status_ok; } template PUGI__FN xml_parse_result load_stream_impl(xml_document_struct* doc, std::basic_istream& stream, unsigned int options, xml_encoding encoding, char_t** out_buffer) { void* buffer = 0; size_t size = 0; xml_parse_status status = status_ok; // if stream has an error bit set, bail out (otherwise tellg() can fail and we'll clear error bits) if (stream.fail()) return make_parse_result(status_io_error); // load stream to memory (using seek-based implementation if possible, since it's faster and takes less memory) if (stream.tellg() < 0) { stream.clear(); // clear error flags that could be set by a failing tellg status = load_stream_data_noseek(stream, &buffer, &size); } else status = load_stream_data_seek(stream, &buffer, &size); if (status != status_ok) return make_parse_result(status); xml_encoding real_encoding = get_buffer_encoding(encoding, buffer, size); return load_buffer_impl(doc, doc, buffer, zero_terminate_buffer(buffer, size, real_encoding), options, real_encoding, true, true, out_buffer); } #endif #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR))) PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) { return _wfopen(path, mode); } #else PUGI__FN char* convert_path_heap(const wchar_t* str) { assert(str); // first pass: get length in utf8 characters size_t length = strlength_wide(str); size_t size = as_utf8_begin(str, length); // allocate resulting string char* result = static_cast(xml_memory::allocate(size + 1)); if (!result) return 0; // second pass: convert to utf8 as_utf8_end(result, size, str, length); // zero-terminate result[size] = 0; return result; } PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) { // there is no standard function to open wide paths, so our best bet is to try utf8 path char* path_utf8 = convert_path_heap(path); if (!path_utf8) return 0; // convert mode to ASCII (we mirror _wfopen interface) char mode_ascii[4] = {0}; for (size_t i = 0; mode[i]; ++i) mode_ascii[i] = static_cast(mode[i]); // try to open the utf8 path FILE* result = fopen(path_utf8, mode_ascii); // free dummy buffer xml_memory::deallocate(path_utf8); return result; } #endif PUGI__FN bool save_file_impl(const xml_document& doc, FILE* file, const char_t* indent, unsigned int flags, xml_encoding encoding) { if (!file) return false; xml_writer_file writer(file); doc.save(writer, indent, flags, encoding); return ferror(file) == 0; } PUGI__NS_END namespace pugi { PUGI__FN xml_writer_file::xml_writer_file(void* file_): file(file_) { } PUGI__FN void xml_writer_file::write(const void* data, size_t size) { size_t result = fwrite(data, 1, size, static_cast(file)); (void)!result; // unfortunately we can't do proper error handling here } #ifndef PUGIXML_NO_STL PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(&stream), wide_stream(0) { } PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(0), wide_stream(&stream) { } PUGI__FN void xml_writer_stream::write(const void* data, size_t size) { if (narrow_stream) { assert(!wide_stream); narrow_stream->write(reinterpret_cast(data), static_cast(size)); } else { assert(wide_stream); assert(size % sizeof(wchar_t) == 0); wide_stream->write(reinterpret_cast(data), static_cast(size / sizeof(wchar_t))); } } #endif PUGI__FN xml_tree_walker::xml_tree_walker(): _depth(0) { } PUGI__FN xml_tree_walker::~xml_tree_walker() { } PUGI__FN int xml_tree_walker::depth() const { return _depth; } PUGI__FN bool xml_tree_walker::begin(xml_node&) { return true; } PUGI__FN bool xml_tree_walker::end(xml_node&) { return true; } PUGI__FN xml_attribute::xml_attribute(): _attr(0) { } PUGI__FN xml_attribute::xml_attribute(xml_attribute_struct* attr): _attr(attr) { } PUGI__FN static void unspecified_bool_xml_attribute(xml_attribute***) { } PUGI__FN xml_attribute::operator xml_attribute::unspecified_bool_type() const { return _attr ? unspecified_bool_xml_attribute : 0; } PUGI__FN bool xml_attribute::operator!() const { return !_attr; } PUGI__FN bool xml_attribute::operator==(const xml_attribute& r) const { return (_attr == r._attr); } PUGI__FN bool xml_attribute::operator!=(const xml_attribute& r) const { return (_attr != r._attr); } PUGI__FN bool xml_attribute::operator<(const xml_attribute& r) const { return (_attr < r._attr); } PUGI__FN bool xml_attribute::operator>(const xml_attribute& r) const { return (_attr > r._attr); } PUGI__FN bool xml_attribute::operator<=(const xml_attribute& r) const { return (_attr <= r._attr); } PUGI__FN bool xml_attribute::operator>=(const xml_attribute& r) const { return (_attr >= r._attr); } PUGI__FN xml_attribute xml_attribute::next_attribute() const { return _attr ? xml_attribute(_attr->next_attribute) : xml_attribute(); } PUGI__FN xml_attribute xml_attribute::previous_attribute() const { return _attr && _attr->prev_attribute_c->next_attribute ? xml_attribute(_attr->prev_attribute_c) : xml_attribute(); } PUGI__FN const char_t* xml_attribute::as_string(const char_t* def) const { return (_attr && _attr->value) ? _attr->value : def; } PUGI__FN int xml_attribute::as_int(int def) const { return (_attr && _attr->value) ? impl::get_value_int(_attr->value) : def; } PUGI__FN unsigned int xml_attribute::as_uint(unsigned int def) const { return (_attr && _attr->value) ? impl::get_value_uint(_attr->value) : def; } PUGI__FN double xml_attribute::as_double(double def) const { return (_attr && _attr->value) ? impl::get_value_double(_attr->value) : def; } PUGI__FN float xml_attribute::as_float(float def) const { return (_attr && _attr->value) ? impl::get_value_float(_attr->value) : def; } PUGI__FN bool xml_attribute::as_bool(bool def) const { return (_attr && _attr->value) ? impl::get_value_bool(_attr->value) : def; } #ifdef PUGIXML_HAS_LONG_LONG PUGI__FN long long xml_attribute::as_llong(long long def) const { return (_attr && _attr->value) ? impl::get_value_llong(_attr->value) : def; } PUGI__FN unsigned long long xml_attribute::as_ullong(unsigned long long def) const { return (_attr && _attr->value) ? impl::get_value_ullong(_attr->value) : def; } #endif PUGI__FN bool xml_attribute::empty() const { return !_attr; } PUGI__FN const char_t* xml_attribute::name() const { return (_attr && _attr->name) ? _attr->name + 0 : PUGIXML_TEXT(""); } PUGI__FN const char_t* xml_attribute::value() const { return (_attr && _attr->value) ? _attr->value + 0 : PUGIXML_TEXT(""); } PUGI__FN size_t xml_attribute::hash_value() const { return static_cast(reinterpret_cast(_attr) / sizeof(xml_attribute_struct)); } PUGI__FN xml_attribute_struct* xml_attribute::internal_object() const { return _attr; } PUGI__FN xml_attribute& xml_attribute::operator=(const char_t* rhs) { set_value(rhs); return *this; } PUGI__FN xml_attribute& xml_attribute::operator=(int rhs) { set_value(rhs); return *this; } PUGI__FN xml_attribute& xml_attribute::operator=(unsigned int rhs) { set_value(rhs); return *this; } PUGI__FN xml_attribute& xml_attribute::operator=(double rhs) { set_value(rhs); return *this; } PUGI__FN xml_attribute& xml_attribute::operator=(float rhs) { set_value(rhs); return *this; } PUGI__FN xml_attribute& xml_attribute::operator=(bool rhs) { set_value(rhs); return *this; } #ifdef PUGIXML_HAS_LONG_LONG PUGI__FN xml_attribute& xml_attribute::operator=(long long rhs) { set_value(rhs); return *this; } PUGI__FN xml_attribute& xml_attribute::operator=(unsigned long long rhs) { set_value(rhs); return *this; } #endif PUGI__FN bool xml_attribute::set_name(const char_t* rhs) { if (!_attr) return false; return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs)); } PUGI__FN bool xml_attribute::set_value(const char_t* rhs) { if (!_attr) return false; return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)); } PUGI__FN bool xml_attribute::set_value(int rhs) { if (!_attr) return false; return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); } PUGI__FN bool xml_attribute::set_value(unsigned int rhs) { if (!_attr) return false; return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); } PUGI__FN bool xml_attribute::set_value(double rhs) { if (!_attr) return false; return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); } PUGI__FN bool xml_attribute::set_value(float rhs) { if (!_attr) return false; return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); } PUGI__FN bool xml_attribute::set_value(bool rhs) { if (!_attr) return false; return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); } #ifdef PUGIXML_HAS_LONG_LONG PUGI__FN bool xml_attribute::set_value(long long rhs) { if (!_attr) return false; return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); } PUGI__FN bool xml_attribute::set_value(unsigned long long rhs) { if (!_attr) return false; return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); } #endif #ifdef __BORLANDC__ PUGI__FN bool operator&&(const xml_attribute& lhs, bool rhs) { return (bool)lhs && rhs; } PUGI__FN bool operator||(const xml_attribute& lhs, bool rhs) { return (bool)lhs || rhs; } #endif PUGI__FN xml_node::xml_node(): _root(0) { } PUGI__FN xml_node::xml_node(xml_node_struct* p): _root(p) { } PUGI__FN static void unspecified_bool_xml_node(xml_node***) { } PUGI__FN xml_node::operator xml_node::unspecified_bool_type() const { return _root ? unspecified_bool_xml_node : 0; } PUGI__FN bool xml_node::operator!() const { return !_root; } PUGI__FN xml_node::iterator xml_node::begin() const { return iterator(_root ? _root->first_child + 0 : 0, _root); } PUGI__FN xml_node::iterator xml_node::end() const { return iterator(0, _root); } PUGI__FN xml_node::attribute_iterator xml_node::attributes_begin() const { return attribute_iterator(_root ? _root->first_attribute + 0 : 0, _root); } PUGI__FN xml_node::attribute_iterator xml_node::attributes_end() const { return attribute_iterator(0, _root); } PUGI__FN xml_object_range xml_node::children() const { return xml_object_range(begin(), end()); } PUGI__FN xml_object_range xml_node::children(const char_t* name_) const { return xml_object_range(xml_named_node_iterator(child(name_)._root, _root, name_), xml_named_node_iterator(0, _root, name_)); } PUGI__FN xml_object_range xml_node::attributes() const { return xml_object_range(attributes_begin(), attributes_end()); } PUGI__FN bool xml_node::operator==(const xml_node& r) const { return (_root == r._root); } PUGI__FN bool xml_node::operator!=(const xml_node& r) const { return (_root != r._root); } PUGI__FN bool xml_node::operator<(const xml_node& r) const { return (_root < r._root); } PUGI__FN bool xml_node::operator>(const xml_node& r) const { return (_root > r._root); } PUGI__FN bool xml_node::operator<=(const xml_node& r) const { return (_root <= r._root); } PUGI__FN bool xml_node::operator>=(const xml_node& r) const { return (_root >= r._root); } PUGI__FN bool xml_node::empty() const { return !_root; } PUGI__FN const char_t* xml_node::name() const { return (_root && _root->name) ? _root->name + 0 : PUGIXML_TEXT(""); } PUGI__FN xml_node_type xml_node::type() const { return _root ? PUGI__NODETYPE(_root) : node_null; } PUGI__FN const char_t* xml_node::value() const { return (_root && _root->value) ? _root->value + 0 : PUGIXML_TEXT(""); } PUGI__FN xml_node xml_node::child(const char_t* name_) const { if (!_root) return xml_node(); for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) if (i->name && impl::strequal(name_, i->name)) return xml_node(i); return xml_node(); } PUGI__FN xml_attribute xml_node::attribute(const char_t* name_) const { if (!_root) return xml_attribute(); for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute) if (i->name && impl::strequal(name_, i->name)) return xml_attribute(i); return xml_attribute(); } PUGI__FN xml_node xml_node::next_sibling(const char_t* name_) const { if (!_root) return xml_node(); for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling) if (i->name && impl::strequal(name_, i->name)) return xml_node(i); return xml_node(); } PUGI__FN xml_node xml_node::next_sibling() const { return _root ? xml_node(_root->next_sibling) : xml_node(); } PUGI__FN xml_node xml_node::previous_sibling(const char_t* name_) const { if (!_root) return xml_node(); for (xml_node_struct* i = _root->prev_sibling_c; i->next_sibling; i = i->prev_sibling_c) if (i->name && impl::strequal(name_, i->name)) return xml_node(i); return xml_node(); } PUGI__FN xml_attribute xml_node::attribute(const char_t* name_, xml_attribute& hint_) const { xml_attribute_struct* hint = hint_._attr; // if hint is not an attribute of node, behavior is not defined assert(!hint || (_root && impl::is_attribute_of(hint, _root))); if (!_root) return xml_attribute(); // optimistically search from hint up until the end for (xml_attribute_struct* i = hint; i; i = i->next_attribute) if (i->name && impl::strequal(name_, i->name)) { // update hint to maximize efficiency of searching for consecutive attributes hint_._attr = i->next_attribute; return xml_attribute(i); } // wrap around and search from the first attribute until the hint // 'j' null pointer check is technically redundant, but it prevents a crash in case the assertion above fails for (xml_attribute_struct* j = _root->first_attribute; j && j != hint; j = j->next_attribute) if (j->name && impl::strequal(name_, j->name)) { // update hint to maximize efficiency of searching for consecutive attributes hint_._attr = j->next_attribute; return xml_attribute(j); } return xml_attribute(); } PUGI__FN xml_node xml_node::previous_sibling() const { if (!_root) return xml_node(); if (_root->prev_sibling_c->next_sibling) return xml_node(_root->prev_sibling_c); else return xml_node(); } PUGI__FN xml_node xml_node::parent() const { return _root ? xml_node(_root->parent) : xml_node(); } PUGI__FN xml_node xml_node::root() const { return _root ? xml_node(&impl::get_document(_root)) : xml_node(); } PUGI__FN xml_text xml_node::text() const { return xml_text(_root); } PUGI__FN const char_t* xml_node::child_value() const { if (!_root) return PUGIXML_TEXT(""); for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) if (impl::is_text_node(i) && i->value) return i->value; return PUGIXML_TEXT(""); } PUGI__FN const char_t* xml_node::child_value(const char_t* name_) const { return child(name_).child_value(); } PUGI__FN xml_attribute xml_node::first_attribute() const { return _root ? xml_attribute(_root->first_attribute) : xml_attribute(); } PUGI__FN xml_attribute xml_node::last_attribute() const { return _root && _root->first_attribute ? xml_attribute(_root->first_attribute->prev_attribute_c) : xml_attribute(); } PUGI__FN xml_node xml_node::first_child() const { return _root ? xml_node(_root->first_child) : xml_node(); } PUGI__FN xml_node xml_node::last_child() const { return _root && _root->first_child ? xml_node(_root->first_child->prev_sibling_c) : xml_node(); } PUGI__FN bool xml_node::set_name(const char_t* rhs) { static const bool has_name[] = { false, false, true, false, false, false, true, true, false }; if (!_root || !has_name[PUGI__NODETYPE(_root)]) return false; return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs)); } PUGI__FN bool xml_node::set_value(const char_t* rhs) { static const bool has_value[] = { false, false, false, true, true, true, true, false, true }; if (!_root || !has_value[PUGI__NODETYPE(_root)]) return false; return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)); } PUGI__FN xml_attribute xml_node::append_attribute(const char_t* name_) { if (!impl::allow_insert_attribute(type())) return xml_attribute(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_attribute(); xml_attribute a(impl::allocate_attribute(alloc)); if (!a) return xml_attribute(); impl::append_attribute(a._attr, _root); a.set_name(name_); return a; } PUGI__FN xml_attribute xml_node::prepend_attribute(const char_t* name_) { if (!impl::allow_insert_attribute(type())) return xml_attribute(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_attribute(); xml_attribute a(impl::allocate_attribute(alloc)); if (!a) return xml_attribute(); impl::prepend_attribute(a._attr, _root); a.set_name(name_); return a; } PUGI__FN xml_attribute xml_node::insert_attribute_after(const char_t* name_, const xml_attribute& attr) { if (!impl::allow_insert_attribute(type())) return xml_attribute(); if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_attribute(); xml_attribute a(impl::allocate_attribute(alloc)); if (!a) return xml_attribute(); impl::insert_attribute_after(a._attr, attr._attr, _root); a.set_name(name_); return a; } PUGI__FN xml_attribute xml_node::insert_attribute_before(const char_t* name_, const xml_attribute& attr) { if (!impl::allow_insert_attribute(type())) return xml_attribute(); if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_attribute(); xml_attribute a(impl::allocate_attribute(alloc)); if (!a) return xml_attribute(); impl::insert_attribute_before(a._attr, attr._attr, _root); a.set_name(name_); return a; } PUGI__FN xml_attribute xml_node::append_copy(const xml_attribute& proto) { if (!proto) return xml_attribute(); if (!impl::allow_insert_attribute(type())) return xml_attribute(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_attribute(); xml_attribute a(impl::allocate_attribute(alloc)); if (!a) return xml_attribute(); impl::append_attribute(a._attr, _root); impl::node_copy_attribute(a._attr, proto._attr); return a; } PUGI__FN xml_attribute xml_node::prepend_copy(const xml_attribute& proto) { if (!proto) return xml_attribute(); if (!impl::allow_insert_attribute(type())) return xml_attribute(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_attribute(); xml_attribute a(impl::allocate_attribute(alloc)); if (!a) return xml_attribute(); impl::prepend_attribute(a._attr, _root); impl::node_copy_attribute(a._attr, proto._attr); return a; } PUGI__FN xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr) { if (!proto) return xml_attribute(); if (!impl::allow_insert_attribute(type())) return xml_attribute(); if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_attribute(); xml_attribute a(impl::allocate_attribute(alloc)); if (!a) return xml_attribute(); impl::insert_attribute_after(a._attr, attr._attr, _root); impl::node_copy_attribute(a._attr, proto._attr); return a; } PUGI__FN xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr) { if (!proto) return xml_attribute(); if (!impl::allow_insert_attribute(type())) return xml_attribute(); if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_attribute(); xml_attribute a(impl::allocate_attribute(alloc)); if (!a) return xml_attribute(); impl::insert_attribute_before(a._attr, attr._attr, _root); impl::node_copy_attribute(a._attr, proto._attr); return a; } PUGI__FN xml_node xml_node::append_child(xml_node_type type_) { if (!impl::allow_insert_child(type(), type_)) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); xml_node n(impl::allocate_node(alloc, type_)); if (!n) return xml_node(); impl::append_node(n._root, _root); if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); return n; } PUGI__FN xml_node xml_node::prepend_child(xml_node_type type_) { if (!impl::allow_insert_child(type(), type_)) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); xml_node n(impl::allocate_node(alloc, type_)); if (!n) return xml_node(); impl::prepend_node(n._root, _root); if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); return n; } PUGI__FN xml_node xml_node::insert_child_before(xml_node_type type_, const xml_node& node) { if (!impl::allow_insert_child(type(), type_)) return xml_node(); if (!node._root || node._root->parent != _root) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); xml_node n(impl::allocate_node(alloc, type_)); if (!n) return xml_node(); impl::insert_node_before(n._root, node._root); if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); return n; } PUGI__FN xml_node xml_node::insert_child_after(xml_node_type type_, const xml_node& node) { if (!impl::allow_insert_child(type(), type_)) return xml_node(); if (!node._root || node._root->parent != _root) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); xml_node n(impl::allocate_node(alloc, type_)); if (!n) return xml_node(); impl::insert_node_after(n._root, node._root); if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); return n; } PUGI__FN xml_node xml_node::append_child(const char_t* name_) { xml_node result = append_child(node_element); result.set_name(name_); return result; } PUGI__FN xml_node xml_node::prepend_child(const char_t* name_) { xml_node result = prepend_child(node_element); result.set_name(name_); return result; } PUGI__FN xml_node xml_node::insert_child_after(const char_t* name_, const xml_node& node) { xml_node result = insert_child_after(node_element, node); result.set_name(name_); return result; } PUGI__FN xml_node xml_node::insert_child_before(const char_t* name_, const xml_node& node) { xml_node result = insert_child_before(node_element, node); result.set_name(name_); return result; } PUGI__FN xml_node xml_node::append_copy(const xml_node& proto) { xml_node_type type_ = proto.type(); if (!impl::allow_insert_child(type(), type_)) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); xml_node n(impl::allocate_node(alloc, type_)); if (!n) return xml_node(); impl::append_node(n._root, _root); impl::node_copy_tree(n._root, proto._root); return n; } PUGI__FN xml_node xml_node::prepend_copy(const xml_node& proto) { xml_node_type type_ = proto.type(); if (!impl::allow_insert_child(type(), type_)) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); xml_node n(impl::allocate_node(alloc, type_)); if (!n) return xml_node(); impl::prepend_node(n._root, _root); impl::node_copy_tree(n._root, proto._root); return n; } PUGI__FN xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node) { xml_node_type type_ = proto.type(); if (!impl::allow_insert_child(type(), type_)) return xml_node(); if (!node._root || node._root->parent != _root) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); xml_node n(impl::allocate_node(alloc, type_)); if (!n) return xml_node(); impl::insert_node_after(n._root, node._root); impl::node_copy_tree(n._root, proto._root); return n; } PUGI__FN xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node) { xml_node_type type_ = proto.type(); if (!impl::allow_insert_child(type(), type_)) return xml_node(); if (!node._root || node._root->parent != _root) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); xml_node n(impl::allocate_node(alloc, type_)); if (!n) return xml_node(); impl::insert_node_before(n._root, node._root); impl::node_copy_tree(n._root, proto._root); return n; } PUGI__FN xml_node xml_node::append_move(const xml_node& moved) { if (!impl::allow_move(*this, moved)) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; impl::remove_node(moved._root); impl::append_node(moved._root, _root); return moved; } PUGI__FN xml_node xml_node::prepend_move(const xml_node& moved) { if (!impl::allow_move(*this, moved)) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; impl::remove_node(moved._root); impl::prepend_node(moved._root, _root); return moved; } PUGI__FN xml_node xml_node::insert_move_after(const xml_node& moved, const xml_node& node) { if (!impl::allow_move(*this, moved)) return xml_node(); if (!node._root || node._root->parent != _root) return xml_node(); if (moved._root == node._root) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; impl::remove_node(moved._root); impl::insert_node_after(moved._root, node._root); return moved; } PUGI__FN xml_node xml_node::insert_move_before(const xml_node& moved, const xml_node& node) { if (!impl::allow_move(*this, moved)) return xml_node(); if (!node._root || node._root->parent != _root) return xml_node(); if (moved._root == node._root) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; impl::remove_node(moved._root); impl::insert_node_before(moved._root, node._root); return moved; } PUGI__FN bool xml_node::remove_attribute(const char_t* name_) { return remove_attribute(attribute(name_)); } PUGI__FN bool xml_node::remove_attribute(const xml_attribute& a) { if (!_root || !a._attr) return false; if (!impl::is_attribute_of(a._attr, _root)) return false; impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return false; impl::remove_attribute(a._attr, _root); impl::destroy_attribute(a._attr, alloc); return true; } PUGI__FN bool xml_node::remove_child(const char_t* name_) { return remove_child(child(name_)); } PUGI__FN bool xml_node::remove_child(const xml_node& n) { if (!_root || !n._root || n._root->parent != _root) return false; impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return false; impl::remove_node(n._root); impl::destroy_node(n._root, alloc); return true; } PUGI__FN xml_parse_result xml_node::append_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) { // append_buffer is only valid for elements/documents if (!impl::allow_insert_child(type(), node_element)) return impl::make_parse_result(status_append_invalid_root); // get document node impl::xml_document_struct* doc = &impl::get_document(_root); // disable document_buffer_order optimization since in a document with multiple buffers comparing buffer pointers does not make sense doc->header |= impl::xml_memory_page_contents_shared_mask; // get extra buffer element (we'll store the document fragment buffer there so that we can deallocate it later) impl::xml_memory_page* page = 0; impl::xml_extra_buffer* extra = static_cast(doc->allocate_memory(sizeof(impl::xml_extra_buffer), page)); (void)page; if (!extra) return impl::make_parse_result(status_out_of_memory); // add extra buffer to the list extra->buffer = 0; extra->next = doc->extra_buffers; doc->extra_buffers = extra; // name of the root has to be NULL before parsing - otherwise closing node mismatches will not be detected at the top level struct name_sentry { xml_node_struct* node; char_t* name; ~name_sentry() { node->name = name; } }; name_sentry sentry = { _root, _root->name }; sentry.node->name = 0; return impl::load_buffer_impl(doc, _root, const_cast(contents), size, options, encoding, false, false, &extra->buffer); } PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* name_, const char_t* attr_name, const char_t* attr_value) const { if (!_root) return xml_node(); for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) if (i->name && impl::strequal(name_, i->name)) { for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT(""))) return xml_node(i); } return xml_node(); } PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const { if (!_root) return xml_node(); for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT(""))) return xml_node(i); return xml_node(); } #ifndef PUGIXML_NO_STL PUGI__FN string_t xml_node::path(char_t delimiter) const { if (!_root) return string_t(); size_t offset = 0; for (xml_node_struct* i = _root; i; i = i->parent) { offset += (i != _root); offset += i->name ? impl::strlength(i->name) : 0; } string_t result; result.resize(offset); for (xml_node_struct* j = _root; j; j = j->parent) { if (j != _root) result[--offset] = delimiter; if (j->name && *j->name) { size_t length = impl::strlength(j->name); offset -= length; memcpy(&result[offset], j->name, length * sizeof(char_t)); } } assert(offset == 0); return result; } #endif PUGI__FN xml_node xml_node::first_element_by_path(const char_t* path_, char_t delimiter) const { xml_node found = *this; // Current search context. if (!_root || !path_ || !path_[0]) return found; if (path_[0] == delimiter) { // Absolute path; e.g. '/foo/bar' found = found.root(); ++path_; } const char_t* path_segment = path_; while (*path_segment == delimiter) ++path_segment; const char_t* path_segment_end = path_segment; while (*path_segment_end && *path_segment_end != delimiter) ++path_segment_end; if (path_segment == path_segment_end) return found; const char_t* next_segment = path_segment_end; while (*next_segment == delimiter) ++next_segment; if (*path_segment == '.' && path_segment + 1 == path_segment_end) return found.first_element_by_path(next_segment, delimiter); else if (*path_segment == '.' && *(path_segment+1) == '.' && path_segment + 2 == path_segment_end) return found.parent().first_element_by_path(next_segment, delimiter); else { for (xml_node_struct* j = found._root->first_child; j; j = j->next_sibling) { if (j->name && impl::strequalrange(j->name, path_segment, static_cast(path_segment_end - path_segment))) { xml_node subsearch = xml_node(j).first_element_by_path(next_segment, delimiter); if (subsearch) return subsearch; } } return xml_node(); } } PUGI__FN bool xml_node::traverse(xml_tree_walker& walker) { walker._depth = -1; xml_node arg_begin = *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 && !cur.parent().empty()) { --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); } PUGI__FN size_t xml_node::hash_value() const { return static_cast(reinterpret_cast(_root) / sizeof(xml_node_struct)); } PUGI__FN xml_node_struct* xml_node::internal_object() const { return _root; } PUGI__FN void xml_node::print(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const { if (!_root) return; impl::xml_buffered_writer buffered_writer(writer, encoding); impl::node_output(buffered_writer, _root, indent, flags, depth); buffered_writer.flush(); } #ifndef PUGIXML_NO_STL PUGI__FN void xml_node::print(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const { xml_writer_stream writer(stream); print(writer, indent, flags, encoding, depth); } PUGI__FN void xml_node::print(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, unsigned int depth) const { xml_writer_stream writer(stream); print(writer, indent, flags, encoding_wchar, depth); } #endif PUGI__FN ptrdiff_t xml_node::offset_debug() const { if (!_root) return -1; impl::xml_document_struct& doc = impl::get_document(_root); // we can determine the offset reliably only if there is exactly once parse buffer if (!doc.buffer || doc.extra_buffers) return -1; switch (type()) { case node_document: return 0; case node_element: case node_declaration: case node_pi: return _root->name && (_root->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0 ? _root->name - doc.buffer : -1; case node_pcdata: case node_cdata: case node_comment: case node_doctype: return _root->value && (_root->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0 ? _root->value - doc.buffer : -1; default: return -1; } } #ifdef __BORLANDC__ PUGI__FN bool operator&&(const xml_node& lhs, bool rhs) { return (bool)lhs && rhs; } PUGI__FN bool operator||(const xml_node& lhs, bool rhs) { return (bool)lhs || rhs; } #endif PUGI__FN xml_text::xml_text(xml_node_struct* root): _root(root) { } PUGI__FN xml_node_struct* xml_text::_data() const { if (!_root || impl::is_text_node(_root)) return _root; for (xml_node_struct* node = _root->first_child; node; node = node->next_sibling) if (impl::is_text_node(node)) return node; return 0; } PUGI__FN xml_node_struct* xml_text::_data_new() { xml_node_struct* d = _data(); if (d) return d; return xml_node(_root).append_child(node_pcdata).internal_object(); } PUGI__FN xml_text::xml_text(): _root(0) { } PUGI__FN static void unspecified_bool_xml_text(xml_text***) { } PUGI__FN xml_text::operator xml_text::unspecified_bool_type() const { return _data() ? unspecified_bool_xml_text : 0; } PUGI__FN bool xml_text::operator!() const { return !_data(); } PUGI__FN bool xml_text::empty() const { return _data() == 0; } PUGI__FN const char_t* xml_text::get() const { xml_node_struct* d = _data(); return (d && d->value) ? d->value + 0 : PUGIXML_TEXT(""); } PUGI__FN const char_t* xml_text::as_string(const char_t* def) const { xml_node_struct* d = _data(); return (d && d->value) ? d->value : def; } PUGI__FN int xml_text::as_int(int def) const { xml_node_struct* d = _data(); return (d && d->value) ? impl::get_value_int(d->value) : def; } PUGI__FN unsigned int xml_text::as_uint(unsigned int def) const { xml_node_struct* d = _data(); return (d && d->value) ? impl::get_value_uint(d->value) : def; } PUGI__FN double xml_text::as_double(double def) const { xml_node_struct* d = _data(); return (d && d->value) ? impl::get_value_double(d->value) : def; } PUGI__FN float xml_text::as_float(float def) const { xml_node_struct* d = _data(); return (d && d->value) ? impl::get_value_float(d->value) : def; } PUGI__FN bool xml_text::as_bool(bool def) const { xml_node_struct* d = _data(); return (d && d->value) ? impl::get_value_bool(d->value) : def; } #ifdef PUGIXML_HAS_LONG_LONG PUGI__FN long long xml_text::as_llong(long long def) const { xml_node_struct* d = _data(); return (d && d->value) ? impl::get_value_llong(d->value) : def; } PUGI__FN unsigned long long xml_text::as_ullong(unsigned long long def) const { xml_node_struct* d = _data(); return (d && d->value) ? impl::get_value_ullong(d->value) : def; } #endif PUGI__FN bool xml_text::set(const char_t* rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)) : false; } PUGI__FN bool xml_text::set(int rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; } PUGI__FN bool xml_text::set(unsigned int rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; } PUGI__FN bool xml_text::set(float rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; } PUGI__FN bool xml_text::set(double rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; } PUGI__FN bool xml_text::set(bool rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; } #ifdef PUGIXML_HAS_LONG_LONG PUGI__FN bool xml_text::set(long long rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; } PUGI__FN bool xml_text::set(unsigned long long rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; } #endif PUGI__FN xml_text& xml_text::operator=(const char_t* rhs) { set(rhs); return *this; } PUGI__FN xml_text& xml_text::operator=(int rhs) { set(rhs); return *this; } PUGI__FN xml_text& xml_text::operator=(unsigned int rhs) { set(rhs); return *this; } PUGI__FN xml_text& xml_text::operator=(double rhs) { set(rhs); return *this; } PUGI__FN xml_text& xml_text::operator=(float rhs) { set(rhs); return *this; } PUGI__FN xml_text& xml_text::operator=(bool rhs) { set(rhs); return *this; } #ifdef PUGIXML_HAS_LONG_LONG PUGI__FN xml_text& xml_text::operator=(long long rhs) { set(rhs); return *this; } PUGI__FN xml_text& xml_text::operator=(unsigned long long rhs) { set(rhs); return *this; } #endif PUGI__FN xml_node xml_text::data() const { return xml_node(_data()); } #ifdef __BORLANDC__ PUGI__FN bool operator&&(const xml_text& lhs, bool rhs) { return (bool)lhs && rhs; } PUGI__FN bool operator||(const xml_text& lhs, bool rhs) { return (bool)lhs || rhs; } #endif PUGI__FN xml_node_iterator::xml_node_iterator() { } PUGI__FN xml_node_iterator::xml_node_iterator(const xml_node& node): _wrap(node), _parent(node.parent()) { } PUGI__FN xml_node_iterator::xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) { } PUGI__FN bool xml_node_iterator::operator==(const xml_node_iterator& rhs) const { return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; } PUGI__FN bool xml_node_iterator::operator!=(const xml_node_iterator& rhs) const { return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; } PUGI__FN xml_node& xml_node_iterator::operator*() const { assert(_wrap._root); return _wrap; } PUGI__FN xml_node* xml_node_iterator::operator->() const { assert(_wrap._root); return const_cast(&_wrap); // BCC32 workaround } PUGI__FN const xml_node_iterator& xml_node_iterator::operator++() { assert(_wrap._root); _wrap._root = _wrap._root->next_sibling; return *this; } PUGI__FN xml_node_iterator xml_node_iterator::operator++(int) { xml_node_iterator temp = *this; ++*this; return temp; } PUGI__FN const xml_node_iterator& xml_node_iterator::operator--() { _wrap = _wrap._root ? _wrap.previous_sibling() : _parent.last_child(); return *this; } PUGI__FN xml_node_iterator xml_node_iterator::operator--(int) { xml_node_iterator temp = *this; --*this; return temp; } PUGI__FN xml_attribute_iterator::xml_attribute_iterator() { } PUGI__FN xml_attribute_iterator::xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent): _wrap(attr), _parent(parent) { } PUGI__FN xml_attribute_iterator::xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) { } PUGI__FN bool xml_attribute_iterator::operator==(const xml_attribute_iterator& rhs) const { return _wrap._attr == rhs._wrap._attr && _parent._root == rhs._parent._root; } PUGI__FN bool xml_attribute_iterator::operator!=(const xml_attribute_iterator& rhs) const { return _wrap._attr != rhs._wrap._attr || _parent._root != rhs._parent._root; } PUGI__FN xml_attribute& xml_attribute_iterator::operator*() const { assert(_wrap._attr); return _wrap; } PUGI__FN xml_attribute* xml_attribute_iterator::operator->() const { assert(_wrap._attr); return const_cast(&_wrap); // BCC32 workaround } PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator++() { assert(_wrap._attr); _wrap._attr = _wrap._attr->next_attribute; return *this; } PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator++(int) { xml_attribute_iterator temp = *this; ++*this; return temp; } PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator--() { _wrap = _wrap._attr ? _wrap.previous_attribute() : _parent.last_attribute(); return *this; } PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator--(int) { xml_attribute_iterator temp = *this; --*this; return temp; } PUGI__FN xml_named_node_iterator::xml_named_node_iterator(): _name(0) { } PUGI__FN xml_named_node_iterator::xml_named_node_iterator(const xml_node& node, const char_t* name): _wrap(node), _parent(node.parent()), _name(name) { } PUGI__FN xml_named_node_iterator::xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name): _wrap(ref), _parent(parent), _name(name) { } PUGI__FN bool xml_named_node_iterator::operator==(const xml_named_node_iterator& rhs) const { return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; } PUGI__FN bool xml_named_node_iterator::operator!=(const xml_named_node_iterator& rhs) const { return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; } PUGI__FN xml_node& xml_named_node_iterator::operator*() const { assert(_wrap._root); return _wrap; } PUGI__FN xml_node* xml_named_node_iterator::operator->() const { assert(_wrap._root); return const_cast(&_wrap); // BCC32 workaround } PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator++() { assert(_wrap._root); _wrap = _wrap.next_sibling(_name); return *this; } PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator++(int) { xml_named_node_iterator temp = *this; ++*this; return temp; } PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator--() { if (_wrap._root) _wrap = _wrap.previous_sibling(_name); else { _wrap = _parent.last_child(); if (!impl::strequal(_wrap.name(), _name)) _wrap = _wrap.previous_sibling(_name); } return *this; } PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator--(int) { xml_named_node_iterator temp = *this; --*this; return temp; } PUGI__FN xml_parse_result::xml_parse_result(): status(status_internal_error), offset(0), encoding(encoding_auto) { } PUGI__FN xml_parse_result::operator bool() const { return status == status_ok; } PUGI__FN const char* xml_parse_result::description() const { switch (status) { case status_ok: return "No error"; case status_file_not_found: return "File was not found"; case status_io_error: return "Error reading from file/stream"; case status_out_of_memory: return "Could not allocate memory"; case status_internal_error: return "Internal error occurred"; case status_unrecognized_tag: return "Could not determine tag type"; case status_bad_pi: return "Error parsing document declaration/processing instruction"; case status_bad_comment: return "Error parsing comment"; case status_bad_cdata: return "Error parsing CDATA section"; case status_bad_doctype: return "Error parsing document type declaration"; case status_bad_pcdata: return "Error parsing PCDATA section"; case status_bad_start_element: return "Error parsing start element tag"; case status_bad_attribute: return "Error parsing element attribute"; case status_bad_end_element: return "Error parsing end element tag"; case status_end_element_mismatch: return "Start-end tags mismatch"; case status_append_invalid_root: return "Unable to append nodes: root is not an element or document"; case status_no_document_element: return "No document element found"; default: return "Unknown error"; } } PUGI__FN xml_document::xml_document(): _buffer(0) { create(); } PUGI__FN xml_document::~xml_document() { destroy(); } PUGI__FN void xml_document::reset() { destroy(); create(); } PUGI__FN void xml_document::reset(const xml_document& proto) { reset(); for (xml_node cur = proto.first_child(); cur; cur = cur.next_sibling()) append_copy(cur); } PUGI__FN void xml_document::create() { assert(!_root); #ifdef PUGIXML_COMPACT const size_t page_offset = sizeof(uint32_t); #else const size_t page_offset = 0; #endif // initialize sentinel page PUGI__STATIC_ASSERT(sizeof(impl::xml_memory_page) + sizeof(impl::xml_document_struct) + impl::xml_memory_page_alignment - sizeof(void*) + page_offset <= sizeof(_memory)); // align upwards to page boundary void* page_memory = reinterpret_cast((reinterpret_cast(_memory) + (impl::xml_memory_page_alignment - 1)) & ~(impl::xml_memory_page_alignment - 1)); // prepare page structure impl::xml_memory_page* page = impl::xml_memory_page::construct(page_memory); assert(page); page->busy_size = impl::xml_memory_page_size; // setup first page marker #ifdef PUGIXML_COMPACT page->compact_page_marker = reinterpret_cast(reinterpret_cast(page) + sizeof(impl::xml_memory_page)); *page->compact_page_marker = sizeof(impl::xml_memory_page); #endif // allocate new root _root = new (reinterpret_cast(page) + sizeof(impl::xml_memory_page) + page_offset) impl::xml_document_struct(page); _root->prev_sibling_c = _root; // setup sentinel page page->allocator = static_cast(_root); // verify the document allocation assert(reinterpret_cast(_root) + sizeof(impl::xml_document_struct) <= _memory + sizeof(_memory)); } PUGI__FN void xml_document::destroy() { assert(_root); // destroy static storage if (_buffer) { impl::xml_memory::deallocate(_buffer); _buffer = 0; } // destroy extra buffers (note: no need to destroy linked list nodes, they're allocated using document allocator) for (impl::xml_extra_buffer* extra = static_cast(_root)->extra_buffers; extra; extra = extra->next) { if (extra->buffer) impl::xml_memory::deallocate(extra->buffer); } // destroy dynamic storage, leave sentinel page (it's in static memory) impl::xml_memory_page* root_page = PUGI__GETPAGE(_root); assert(root_page && !root_page->prev); assert(reinterpret_cast(root_page) >= _memory && reinterpret_cast(root_page) < _memory + sizeof(_memory)); for (impl::xml_memory_page* page = root_page->next; page; ) { impl::xml_memory_page* next = page->next; impl::xml_allocator::deallocate_page(page); page = next; } #ifdef PUGIXML_COMPACT // destroy hash table static_cast(_root)->hash.clear(); #endif _root = 0; } #ifndef PUGIXML_NO_STL PUGI__FN xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options, xml_encoding encoding) { reset(); return impl::load_stream_impl(static_cast(_root), stream, options, encoding, &_buffer); } PUGI__FN xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options) { reset(); return impl::load_stream_impl(static_cast(_root), stream, options, encoding_wchar, &_buffer); } #endif PUGI__FN xml_parse_result xml_document::load_string(const char_t* contents, unsigned int options) { // Force native encoding (skip autodetection) #ifdef PUGIXML_WCHAR_MODE xml_encoding encoding = encoding_wchar; #else xml_encoding encoding = encoding_utf8; #endif return load_buffer(contents, impl::strlength(contents) * sizeof(char_t), options, encoding); } PUGI__FN xml_parse_result xml_document::load(const char_t* contents, unsigned int options) { return load_string(contents, options); } PUGI__FN xml_parse_result xml_document::load_file(const char* path_, unsigned int options, xml_encoding encoding) { reset(); using impl::auto_deleter; // MSVC7 workaround auto_deleter file(fopen(path_, "rb"), fclose); return impl::load_file_impl(static_cast(_root), file.data, options, encoding, &_buffer); } PUGI__FN xml_parse_result xml_document::load_file(const wchar_t* path_, unsigned int options, xml_encoding encoding) { reset(); using impl::auto_deleter; // MSVC7 workaround auto_deleter file(impl::open_file_wide(path_, L"rb"), fclose); return impl::load_file_impl(static_cast(_root), file.data, options, encoding, &_buffer); } PUGI__FN xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) { reset(); return impl::load_buffer_impl(static_cast(_root), _root, const_cast(contents), size, options, encoding, false, false, &_buffer); } PUGI__FN xml_parse_result xml_document::load_buffer_inplace(void* contents, size_t size, unsigned int options, xml_encoding encoding) { reset(); return impl::load_buffer_impl(static_cast(_root), _root, contents, size, options, encoding, true, false, &_buffer); } PUGI__FN xml_parse_result xml_document::load_buffer_inplace_own(void* contents, size_t size, unsigned int options, xml_encoding encoding) { reset(); return impl::load_buffer_impl(static_cast(_root), _root, contents, size, options, encoding, true, true, &_buffer); } PUGI__FN void xml_document::save(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding) const { impl::xml_buffered_writer buffered_writer(writer, encoding); if ((flags & format_write_bom) && encoding != encoding_latin1) { // BOM always represents the codepoint U+FEFF, so just write it in native encoding #ifdef PUGIXML_WCHAR_MODE unsigned int bom = 0xfeff; buffered_writer.write(static_cast(bom)); #else buffered_writer.write('\xef', '\xbb', '\xbf'); #endif } if (!(flags & format_no_declaration) && !impl::has_declaration(_root)) { buffered_writer.write_string(PUGIXML_TEXT("'); if (!(flags & format_raw)) buffered_writer.write('\n'); } impl::node_output(buffered_writer, _root, indent, flags, 0); buffered_writer.flush(); } #ifndef PUGIXML_NO_STL PUGI__FN void xml_document::save(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding) const { xml_writer_stream writer(stream); save(writer, indent, flags, encoding); } PUGI__FN void xml_document::save(std::basic_ostream >& stream, const char_t* indent, unsigned int flags) const { xml_writer_stream writer(stream); save(writer, indent, flags, encoding_wchar); } #endif PUGI__FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const { using impl::auto_deleter; // MSVC7 workaround auto_deleter file(fopen(path_, (flags & format_save_file_text) ? "w" : "wb"), fclose); return impl::save_file_impl(*this, file.data, indent, flags, encoding); } PUGI__FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const { using impl::auto_deleter; // MSVC7 workaround auto_deleter file(impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb"), fclose); return impl::save_file_impl(*this, file.data, indent, flags, encoding); } PUGI__FN xml_node xml_document::document_element() const { assert(_root); for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) if (PUGI__NODETYPE(i) == node_element) return xml_node(i); return xml_node(); } #ifndef PUGIXML_NO_STL PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const wchar_t* str) { assert(str); return impl::as_utf8_impl(str, impl::strlength_wide(str)); } PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const std::basic_string& str) { return impl::as_utf8_impl(str.c_str(), str.size()); } PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const char* str) { assert(str); return impl::as_wide_impl(str, strlen(str)); } PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const std::string& str) { return impl::as_wide_impl(str.c_str(), str.size()); } #endif PUGI__FN void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate) { impl::xml_memory::allocate = allocate; impl::xml_memory::deallocate = deallocate; } PUGI__FN allocation_function PUGIXML_FUNCTION get_memory_allocation_function() { return impl::xml_memory::allocate; } PUGI__FN deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function() { return impl::xml_memory::deallocate; } } #if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC)) namespace std { // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_node_iterator&) { return std::bidirectional_iterator_tag(); } PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_attribute_iterator&) { return std::bidirectional_iterator_tag(); } PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_named_node_iterator&) { return std::bidirectional_iterator_tag(); } } #endif #if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) namespace std { // Workarounds for (non-standard) iterator category detection PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_node_iterator&) { return std::bidirectional_iterator_tag(); } PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_attribute_iterator&) { return std::bidirectional_iterator_tag(); } PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_named_node_iterator&) { return std::bidirectional_iterator_tag(); } } #endif #ifndef PUGIXML_NO_XPATH // STL replacements PUGI__NS_BEGIN struct equal_to { template bool operator()(const T& lhs, const T& rhs) const { return lhs == rhs; } }; struct not_equal_to { template bool operator()(const T& lhs, const T& rhs) const { return lhs != rhs; } }; struct less { template bool operator()(const T& lhs, const T& rhs) const { return lhs < rhs; } }; struct less_equal { template bool operator()(const T& lhs, const T& rhs) const { return lhs <= rhs; } }; template 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 (end - begin > 1) swap(*begin++, *--end); } template I unique(I begin, I end) { // fast skip head while (end - begin > 1 && *begin != *(begin + 1)) begin++; if (begin == end) return begin; // last written element I write = begin++; // merge unique elements while (begin != end) { if (*begin != *write) *++write = *begin++; else begin++; } // past-the-end (write points to live element) return write + 1; } template 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); } PUGI__NS_END // Allocator used for AST and evaluation stacks PUGI__NS_BEGIN static const size_t xpath_memory_page_size = #ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE PUGIXML_MEMORY_XPATH_PAGE_SIZE #else 4096 #endif ; static const uintptr_t xpath_memory_block_alignment = sizeof(double) > sizeof(void*) ? sizeof(double) : sizeof(void*); struct xpath_memory_block { xpath_memory_block* next; size_t capacity; union { char data[xpath_memory_page_size]; double alignment; }; }; 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) { // round size up to block alignment boundary size = (size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1); if (_root_size + size <= _root->capacity) { void* buf = &_root->data[0] + _root_size; _root_size += size; return buf; } else { // make sure we have at least 1/4th of the page free after allocation to satisfy subsequent allocation requests size_t block_capacity_base = sizeof(_root->data); size_t block_capacity_req = size + block_capacity_base / 4; size_t block_capacity = (block_capacity_base > block_capacity_req) ? block_capacity_base : block_capacity_req; size_t block_size = block_capacity + offsetof(xpath_memory_block, data); xpath_memory_block* block = static_cast(xml_memory::allocate(block_size)); if (!block) return 0; block->next = _root; block->capacity = block_capacity; _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) { // round size up to block alignment boundary old_size = (old_size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1); new_size = (new_size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1); // we can only reallocate the last object assert(ptr == 0 || static_cast(ptr) + old_size == &_root->data[0] + _root_size); // 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 xml_memory::deallocate(_root->next); _root->next = next; } } } return result; } void revert(const xpath_allocator& state) { // free all new pages xpath_memory_block* cur = _root; while (cur != state._root) { xpath_memory_block* next = cur->next; xml_memory::deallocate(cur); cur = next; } // restore state _root = state._root; _root_size = state._root_size; } void release() { xpath_memory_block* cur = _root; assert(cur); while (cur->next) { xpath_memory_block* next = cur->next; xml_memory::deallocate(cur); cur = next; } } }; struct xpath_allocator_capture { xpath_allocator_capture(xpath_allocator* alloc): _target(alloc), _state(*alloc) { } ~xpath_allocator_capture() { _target->revert(_state); } xpath_allocator* _target; xpath_allocator _state; }; struct xpath_stack { xpath_allocator* result; xpath_allocator* temp; }; struct xpath_stack_data { xpath_memory_block blocks[2]; xpath_allocator result; xpath_allocator temp; xpath_stack stack; #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; blocks[0].capacity = blocks[1].capacity = sizeof(blocks[0].data); 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(); } }; PUGI__NS_END // String class PUGI__NS_BEGIN class xpath_string { const char_t* _buffer; bool _uses_heap; size_t _length_heap; static char_t* duplicate_string(const char_t* string, size_t length, xpath_allocator* alloc) { char_t* result = static_cast(alloc->allocate((length + 1) * sizeof(char_t))); assert(result); memcpy(result, string, length * sizeof(char_t)); result[length] = 0; return result; } xpath_string(const char_t* buffer, bool uses_heap_, size_t length_heap): _buffer(buffer), _uses_heap(uses_heap_), _length_heap(length_heap) { } public: static xpath_string from_const(const char_t* str) { return xpath_string(str, false, 0); } static xpath_string from_heap_preallocated(const char_t* begin, const char_t* end) { assert(begin <= end && *end == 0); return xpath_string(begin, true, static_cast(end - begin)); } static xpath_string from_heap(const char_t* begin, const char_t* end, xpath_allocator* alloc) { assert(begin <= end); size_t length = static_cast(end - begin); return length == 0 ? xpath_string() : xpath_string(duplicate_string(begin, length, alloc), true, length); } xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false), _length_heap(0) { } void append(const xpath_string& o, xpath_allocator* alloc) { // skip empty sources if (!*o._buffer) return; // fast append for constant empty target and constant source if (!*_buffer && !_uses_heap && !o._uses_heap) { _buffer = o._buffer; } else { // need to make heap copy size_t target_length = length(); size_t source_length = o.length(); size_t result_length = target_length + source_length; // allocate new buffer char_t* result = static_cast(alloc->reallocate(_uses_heap ? const_cast(_buffer) : 0, (target_length + 1) * sizeof(char_t), (result_length + 1) * sizeof(char_t))); 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[result_length] = 0; // finalize _buffer = result; _uses_heap = true; _length_heap = result_length; } } const char_t* c_str() const { return _buffer; } size_t length() const { return _uses_heap ? _length_heap : strlength(_buffer); } char_t* data(xpath_allocator* alloc) { // make private heap copy if (!_uses_heap) { size_t length_ = strlength(_buffer); _buffer = duplicate_string(_buffer, length_, alloc); _uses_heap = true; _length_heap = length_; } return const_cast(_buffer); } bool empty() const { return *_buffer == 0; } bool operator==(const xpath_string& o) const { return strequal(_buffer, o._buffer); } bool operator!=(const xpath_string& o) const { return !strequal(_buffer, o._buffer); } bool uses_heap() const { return _uses_heap; } }; PUGI__NS_END PUGI__NS_BEGIN PUGI__FN bool starts_with(const char_t* string, const char_t* pattern) { while (*pattern && *string == *pattern) { string++; pattern++; } return *pattern == 0; } PUGI__FN const char_t* find_char(const char_t* s, char_t c) { #ifdef PUGIXML_WCHAR_MODE return wcschr(s, c); #else return strchr(s, c); #endif } PUGI__FN const char_t* find_substring(const char_t* s, const char_t* p) { #ifdef PUGIXML_WCHAR_MODE // MSVC6 wcsstr bug workaround (if s is empty it always returns 0) return (*p == 0) ? s : wcsstr(s, p); #else return strstr(s, p); #endif } // Converts symbol to lower case, if it is an ASCII one PUGI__FN char_t tolower_ascii(char_t ch) { return static_cast(ch - 'A') < 26 ? static_cast(ch | ' ') : ch; } PUGI__FN xpath_string string_value(const xpath_node& na, xpath_allocator* alloc) { if (na.attribute()) return xpath_string::from_const(na.attribute().value()); else { xml_node n = na.node(); switch (n.type()) { case node_pcdata: case node_cdata: case node_comment: case node_pi: return xpath_string::from_const(n.value()); case node_document: case node_element: { xpath_string result; xml_node cur = n.first_child(); while (cur && cur != n) { if (cur.type() == node_pcdata || cur.type() == node_cdata) result.append(xpath_string::from_const(cur.value()), alloc); if (cur.first_child()) cur = cur.first_child(); else if (cur.next_sibling()) cur = cur.next_sibling(); else { while (!cur.next_sibling() && cur != n) cur = cur.parent(); if (cur != n) cur = cur.next_sibling(); } } return result; } default: return xpath_string(); } } } PUGI__FN bool node_is_before_sibling(xml_node_struct* ln, xml_node_struct* rn) { assert(ln->parent == rn->parent); // there is no common ancestor (the shared parent is null), nodes are from different documents if (!ln->parent) return ln < rn; // determine sibling order xml_node_struct* ls = ln; xml_node_struct* rs = rn; while (ls && rs) { if (ls == rn) return true; if (rs == ln) return false; ls = ls->next_sibling; rs = rs->next_sibling; } // if rn sibling chain ended ln must be before rn return !rs; } PUGI__FN bool node_is_before(xml_node_struct* ln, xml_node_struct* rn) { // find common ancestor at the same depth, if any xml_node_struct* lp = ln; xml_node_struct* rp = rn; while (lp && rp && lp->parent != rp->parent) { lp = lp->parent; rp = rp->parent; } // parents are the same! if (lp && rp) return node_is_before_sibling(lp, rp); // nodes are at different depths, need to normalize heights bool left_higher = !lp; while (lp) { lp = lp->parent; ln = ln->parent; } while (rp) { rp = rp->parent; rn = rn->parent; } // one node is the ancestor of the other if (ln == rn) return left_higher; // find common ancestor... again while (ln->parent != rn->parent) { ln = ln->parent; rn = rn->parent; } return node_is_before_sibling(ln, rn); } PUGI__FN bool node_is_ancestor(xml_node_struct* parent, xml_node_struct* node) { while (node && node != parent) node = node->parent; return parent && node == parent; } PUGI__FN const void* document_buffer_order(const xpath_node& xnode) { xml_node_struct* node = xnode.node().internal_object(); if (node) { if ((get_document(node).header & xml_memory_page_contents_shared_mask) == 0) { if (node->name && (node->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0) return node->name; if (node->value && (node->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0) return node->value; } return 0; } xml_attribute_struct* attr = xnode.attribute().internal_object(); if (attr) { if ((get_document(attr).header & xml_memory_page_contents_shared_mask) == 0) { if ((attr->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0) return attr->name; if ((attr->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0) return attr->value; } return 0; } return 0; } struct document_order_comparator { bool operator()(const xpath_node& lhs, const xpath_node& rhs) const { // optimized document order based check const void* lo = document_buffer_order(lhs); const void* ro = document_buffer_order(rhs); if (lo && ro) return lo < ro; // slow comparison xml_node ln = lhs.node(), rn = rhs.node(); // compare attributes if (lhs.attribute() && rhs.attribute()) { // shared parent if (lhs.parent() == rhs.parent()) { // determine sibling order for (xml_attribute a = lhs.attribute(); a; a = a.next_attribute()) if (a == rhs.attribute()) return true; return false; } // compare attribute parents ln = lhs.parent(); rn = rhs.parent(); } else if (lhs.attribute()) { // attributes go after the parent element if (lhs.parent() == rhs.node()) return false; ln = lhs.parent(); } else if (rhs.attribute()) { // attributes go after the parent element if (rhs.parent() == lhs.node()) return true; rn = rhs.parent(); } if (ln == rn) return false; if (!ln || !rn) return ln < rn; return node_is_before(ln.internal_object(), rn.internal_object()); } }; 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(); } }; PUGI__FN 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; uint32_t i; } u[sizeof(float) == sizeof(uint32_t) ? 1 : -1]; u[0].i = 0x7fc00000; return u[0].f; #else // fallback const volatile double zero = 0.0; return zero / zero; #endif } PUGI__FN bool is_nan(double value) { #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) return !!_isnan(value); #elif defined(fpclassify) && defined(FP_NAN) return fpclassify(value) == FP_NAN; #else // fallback const volatile double v = value; return v != v; #endif } PUGI__FN const char_t* convert_number_to_string_special(double value) { #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) if (_finite(value)) return (value == 0) ? PUGIXML_TEXT("0") : 0; if (_isnan(value)) return PUGIXML_TEXT("NaN"); return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); #elif defined(fpclassify) && defined(FP_NAN) && defined(FP_INFINITE) && defined(FP_ZERO) switch (fpclassify(value)) { case FP_NAN: return PUGIXML_TEXT("NaN"); case FP_INFINITE: return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); case FP_ZERO: return PUGIXML_TEXT("0"); default: return 0; } #else // fallback const volatile double v = value; if (v == 0) return PUGIXML_TEXT("0"); if (v != v) return PUGIXML_TEXT("NaN"); if (v * 2 == v) return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); return 0; #endif } PUGI__FN bool convert_number_to_boolean(double value) { return (value != 0 && !is_nan(value)); } PUGI__FN void truncate_zeros(char* begin, char* end) { while (begin != end && end[-1] == '0') end--; *end = 0; } // gets mantissa digits in the form of 0.xxxxx with 0. implied and the exponent #if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE) PUGI__FN void convert_number_to_mantissa_exponent(double value, char* buffer, 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 PUGI__FN 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 PUGI__FN xpath_string convert_number_to_string(double value, xpath_allocator* alloc) { // try special number conversion const char_t* special = convert_number_to_string_special(value); if (special) return xpath_string::from_const(special); // get mantissa + exponent form char mantissa_buffer[32]; char* mantissa; int exponent; convert_number_to_mantissa_exponent(value, mantissa_buffer, sizeof(mantissa_buffer), &mantissa, &exponent); // allocate a buffer of suitable length for the number size_t result_size = strlen(mantissa_buffer) + (exponent > 0 ? exponent : -exponent) + 4; char_t* result = static_cast(alloc->allocate(sizeof(char_t) * result_size)); assert(result); // make the number! char_t* s = result; // sign if (value < 0) *s++ = '-'; // integer part if (exponent <= 0) { *s++ = '0'; } else { while (exponent > 0) { assert(*mantissa == 0 || static_cast(static_cast(*mantissa) - '0') <= 9); *s++ = *mantissa ? *mantissa++ : '0'; exponent--; } } // fractional part if (*mantissa) { // decimal point *s++ = '.'; // extra zeroes from negative exponent while (exponent < 0) { *s++ = '0'; exponent++; } // extra mantissa digits while (*mantissa) { assert(static_cast(*mantissa - '0') <= 9); *s++ = *mantissa++; } } // zero-terminate assert(s < result + result_size); *s = 0; return xpath_string::from_heap_preallocated(result, s); } PUGI__FN bool check_string_to_number_format(const char_t* string) { // parse leading whitespace while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string; // parse sign if (*string == '-') ++string; if (!*string) return false; // if there is no integer part, there should be a decimal part with at least one digit if (!PUGI__IS_CHARTYPEX(string[0], ctx_digit) && (string[0] != '.' || !PUGI__IS_CHARTYPEX(string[1], ctx_digit))) return false; // parse integer part while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string; // parse decimal part if (*string == '.') { ++string; while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string; } // parse trailing whitespace while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string; return *string == 0; } PUGI__FN double convert_string_to_number(const char_t* string) { // check string format if (!check_string_to_number_format(string)) return gen_nan(); // parse string #ifdef PUGIXML_WCHAR_MODE return wcstod(string, 0); #else return strtod(string, 0); #endif } PUGI__FN bool convert_string_to_number_scratch(char_t (&buffer)[32], const char_t* begin, const char_t* end, double* out_result) { size_t length = static_cast(end - begin); char_t* scratch = buffer; if (length >= sizeof(buffer) / sizeof(buffer[0])) { // need to make dummy on-heap copy scratch = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!scratch) return false; } // copy string to zero-terminated buffer and perform conversion memcpy(scratch, begin, length * sizeof(char_t)); scratch[length] = 0; *out_result = convert_string_to_number(scratch); // free dummy buffer if (scratch != buffer) xml_memory::deallocate(scratch); return true; } PUGI__FN double round_nearest(double value) { return floor(value + 0.5); } PUGI__FN double round_nearest_nzero(double value) { // same as round_nearest, but returns -0 for [-0.5, -0] // ceil is used to differentiate between +0 and -0 (we return -0 for [-0.5, -0] and +0 for +0) return (value >= -0.5 && value <= 0) ? ceil(value) : floor(value + 0.5); } PUGI__FN const char_t* qualified_name(const xpath_node& node) { return node.attribute() ? node.attribute().name() : node.node().name(); } PUGI__FN const char_t* local_name(const xpath_node& node) { const char_t* name = qualified_name(node); const char_t* p = find_char(name, ':'); return p ? p + 1 : name; } struct namespace_uri_predicate { const char_t* prefix; size_t prefix_length; namespace_uri_predicate(const char_t* name) { const char_t* pos = find_char(name, ':'); prefix = pos ? name : 0; prefix_length = pos ? static_cast(pos - name) : 0; } bool operator()(xml_attribute a) const { const char_t* name = a.name(); if (!starts_with(name, PUGIXML_TEXT("xmlns"))) return false; return prefix ? name[5] == ':' && strequalrange(name + 6, prefix, prefix_length) : name[5] == 0; } }; PUGI__FN const char_t* namespace_uri(xml_node node) { namespace_uri_predicate pred = node.name(); xml_node p = node; while (p) { xml_attribute a = p.find_attribute(pred); if (a) return a.value(); p = p.parent(); } return PUGIXML_TEXT(""); } PUGI__FN const char_t* namespace_uri(xml_attribute attr, xml_node parent) { namespace_uri_predicate pred = attr.name(); // Default namespace does not apply to attributes if (!pred.prefix) return PUGIXML_TEXT(""); xml_node p = parent; while (p) { xml_attribute a = p.find_attribute(pred); if (a) return a.value(); p = p.parent(); } return PUGIXML_TEXT(""); } PUGI__FN const char_t* namespace_uri(const xpath_node& node) { return node.attribute() ? namespace_uri(node.attribute(), node.parent()) : namespace_uri(node.node()); } PUGI__FN char_t* normalize_space(char_t* buffer) { char_t* write = buffer; for (char_t* it = buffer; *it; ) { char_t ch = *it++; if (PUGI__IS_CHARTYPE(ch, ct_space)) { // replace whitespace sequence with single space while (PUGI__IS_CHARTYPE(*it, ct_space)) it++; // avoid leading spaces if (write != buffer) *write++ = ' '; } else *write++ = ch; } // remove trailing space if (write != buffer && PUGI__IS_CHARTYPE(write[-1], ct_space)) write--; // zero-terminate *write = 0; return write; } PUGI__FN char_t* translate(char_t* buffer, const char_t* from, const char_t* to, size_t to_length) { char_t* write = buffer; while (*buffer) { PUGI__DMC_VOLATILE char_t ch = *buffer++; const char_t* pos = find_char(from, ch); if (!pos) *write++ = ch; // do not process else if (static_cast(pos - from) < to_length) *write++ = to[pos - from]; // replace } // zero-terminate *write = 0; return write; } PUGI__FN unsigned char* translate_table_generate(xpath_allocator* alloc, const char_t* from, const char_t* to) { unsigned char table[128] = {0}; while (*from) { unsigned int fc = static_cast(*from); unsigned int tc = static_cast(*to); if (fc >= 128 || tc >= 128) return 0; // code=128 means "skip character" if (!table[fc]) table[fc] = static_cast(tc ? tc : 128); from++; if (tc) to++; } for (int i = 0; i < 128; ++i) if (!table[i]) table[i] = static_cast(i); void* result = alloc->allocate_nothrow(sizeof(table)); if (result) { memcpy(result, table, sizeof(table)); } return static_cast(result); } PUGI__FN char_t* translate_table(char_t* buffer, const unsigned char* table) { char_t* write = buffer; while (*buffer) { char_t ch = *buffer++; unsigned int index = static_cast(ch); if (index < 128) { unsigned char code = table[index]; // code=128 means "skip character" (table size is 128 so 128 can be a special value) // this code skips these characters without extra branches *write = static_cast(code); write += 1 - (code >> 7); } else { *write++ = ch; } } // zero-terminate *write = 0; return write; } inline bool is_xpath_attribute(const char_t* name) { return !(starts_with(name, PUGIXML_TEXT("xmlns")) && (name[5] == 0 || name[5] == ':')); } struct xpath_variable_boolean: xpath_variable { xpath_variable_boolean(): xpath_variable(xpath_type_boolean), value(false) { } bool value; char_t name[1]; }; struct xpath_variable_number: xpath_variable { xpath_variable_number(): xpath_variable(xpath_type_number), value(0) { } double value; char_t name[1]; }; struct xpath_variable_string: xpath_variable { xpath_variable_string(): xpath_variable(xpath_type_string), value(0) { } ~xpath_variable_string() { if (value) xml_memory::deallocate(value); } char_t* value; char_t name[1]; }; struct xpath_variable_node_set: xpath_variable { xpath_variable_node_set(): xpath_variable(xpath_type_node_set) { } xpath_node_set value; char_t name[1]; }; static const xpath_node_set dummy_node_set; PUGI__FN unsigned int hash_string(const char_t* str) { // Jenkins one-at-a-time hash (http://en.wikipedia.org/wiki/Jenkins_hash_function#one-at-a-time) unsigned int result = 0; while (*str) { result += static_cast(*str++); result += result << 10; result ^= result >> 6; } result += result << 3; result ^= result >> 11; result += result << 15; return result; } template PUGI__FN T* new_xpath_variable(const char_t* name) { size_t length = strlength(name); if (length == 0) return 0; // empty variable names are invalid // $$ we can't use offsetof(T, name) because T is non-POD, so we just allocate additional length characters void* memory = xml_memory::allocate(sizeof(T) + length * sizeof(char_t)); if (!memory) return 0; T* result = new (memory) T(); memcpy(result->name, name, (length + 1) * sizeof(char_t)); return result; } PUGI__FN xpath_variable* new_xpath_variable(xpath_value_type type, const char_t* name) { switch (type) { case xpath_type_node_set: return new_xpath_variable(name); case xpath_type_number: return new_xpath_variable(name); case xpath_type_string: return new_xpath_variable(name); case xpath_type_boolean: return new_xpath_variable(name); default: return 0; } } template PUGI__FN void delete_xpath_variable(T* var) { var->~T(); xml_memory::deallocate(var); } PUGI__FN void delete_xpath_variable(xpath_value_type type, xpath_variable* var) { switch (type) { case xpath_type_node_set: delete_xpath_variable(static_cast(var)); break; case xpath_type_number: delete_xpath_variable(static_cast(var)); break; case xpath_type_string: delete_xpath_variable(static_cast(var)); break; case xpath_type_boolean: delete_xpath_variable(static_cast(var)); break; default: assert(!"Invalid variable type"); } } PUGI__FN bool copy_xpath_variable(xpath_variable* lhs, const xpath_variable* rhs) { switch (rhs->type()) { case xpath_type_node_set: return lhs->set(static_cast(rhs)->value); case xpath_type_number: return lhs->set(static_cast(rhs)->value); case xpath_type_string: return lhs->set(static_cast(rhs)->value); case xpath_type_boolean: return lhs->set(static_cast(rhs)->value); default: assert(!"Invalid variable type"); return false; } } PUGI__FN bool get_variable_scratch(char_t (&buffer)[32], xpath_variable_set* set, const char_t* begin, const char_t* end, xpath_variable** out_result) { size_t length = static_cast(end - begin); char_t* scratch = buffer; if (length >= sizeof(buffer) / sizeof(buffer[0])) { // need to make dummy on-heap copy scratch = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!scratch) return false; } // copy string to zero-terminated buffer and perform lookup memcpy(scratch, begin, length * sizeof(char_t)); scratch[length] = 0; *out_result = set->get(scratch); // free dummy buffer if (scratch != buffer) xml_memory::deallocate(scratch); return true; } PUGI__NS_END // Internal node set class PUGI__NS_BEGIN PUGI__FN xpath_node_set::type_t xpath_get_order(const xpath_node* begin, const xpath_node* end) { if (end - begin < 2) return xpath_node_set::type_sorted; document_order_comparator cmp; bool first = cmp(begin[0], begin[1]); for (const xpath_node* it = begin + 1; it + 1 < end; ++it) if (cmp(it[0], it[1]) != first) return xpath_node_set::type_unsorted; return first ? xpath_node_set::type_sorted : xpath_node_set::type_sorted_reverse; } PUGI__FN xpath_node_set::type_t xpath_sort(xpath_node* begin, xpath_node* end, xpath_node_set::type_t type, bool rev) { xpath_node_set::type_t order = rev ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted; if (type == xpath_node_set::type_unsorted) { xpath_node_set::type_t sorted = xpath_get_order(begin, end); if (sorted == xpath_node_set::type_unsorted) { sort(begin, end, document_order_comparator()); type = xpath_node_set::type_sorted; } else type = sorted; } if (type != order) reverse(begin, end); return order; } PUGI__FN xpath_node xpath_first(const xpath_node* begin, const xpath_node* end, xpath_node_set::type_t type) { if (begin == end) return xpath_node(); switch (type) { case xpath_node_set::type_sorted: return *begin; case xpath_node_set::type_sorted_reverse: return *(end - 1); case xpath_node_set::type_unsorted: return *min_element(begin, end, document_order_comparator()); default: assert(!"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_grow(const xpath_node& node, xpath_allocator* alloc); void push_back(const xpath_node& node, xpath_allocator* alloc) { if (_end != _eos) *_end++ = node; else push_back_grow(node, alloc); } void append(const xpath_node* begin_, const xpath_node* end_, xpath_allocator* alloc) { if (begin_ == end_) return; size_t size_ = static_cast(_end - _begin); size_t capacity = static_cast(_eos - _begin); size_t count = static_cast(end_ - begin_); if (size_ + count > capacity) { // reallocate the old array or allocate a new one xpath_node* data = static_cast(alloc->reallocate(_begin, capacity * sizeof(xpath_node), (size_ + count) * sizeof(xpath_node))); 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 value) { _type = value; } }; PUGI__FN_NO_INLINE void xpath_node_set_raw::push_back_grow(const xpath_node& node, xpath_allocator* alloc) { size_t capacity = static_cast(_eos - _begin); // get new capacity (1.5x rule) size_t new_capacity = capacity + capacity / 2 + 1; // reallocate the old array or allocate a new one xpath_node* data = static_cast(alloc->reallocate(_begin, capacity * sizeof(xpath_node), new_capacity * sizeof(xpath_node))); assert(data); // finalize _begin = data; _end = data + capacity; _eos = data + new_capacity; // push *_end++ = node; } PUGI__NS_END PUGI__NS_BEGIN struct xpath_context { xpath_node n; size_t position, size; xpath_context(const xpath_node& n_, size_t position_, size_t size_): n(n_), position(position_), size(size_) { } }; enum lexeme_t { lex_none = 0, lex_equal, lex_not_equal, lex_less, lex_greater, lex_less_or_equal, lex_greater_or_equal, lex_plus, lex_minus, lex_multiply, lex_union, lex_var_ref, lex_open_brace, lex_close_brace, lex_quoted_string, lex_number, lex_slash, lex_double_slash, lex_open_square_brace, lex_close_square_brace, lex_string, lex_comma, lex_axis_attribute, lex_dot, lex_double_dot, lex_double_colon, lex_eof }; struct xpath_lexer_string { const char_t* begin; const char_t* end; xpath_lexer_string(): begin(0), end(0) { } bool operator==(const char_t* other) const { size_t length = static_cast(end - begin); return strequalrange(other, begin, length); } }; class xpath_lexer { const char_t* _cur; const char_t* _cur_lexeme_pos; xpath_lexer_string _cur_lexeme_contents; lexeme_t _cur_lexeme; public: explicit xpath_lexer(const char_t* query): _cur(query) { next(); } const char_t* state() const { return _cur; } void next() { const char_t* cur = _cur; while (PUGI__IS_CHARTYPE(*cur, ct_space)) ++cur; // save lexeme position for error reporting _cur_lexeme_pos = cur; switch (*cur) { case 0: _cur_lexeme = lex_eof; break; case '>': if (*(cur+1) == '=') { cur += 2; _cur_lexeme = lex_greater_or_equal; } else { cur += 1; _cur_lexeme = lex_greater; } break; case '<': if (*(cur+1) == '=') { cur += 2; _cur_lexeme = lex_less_or_equal; } else { cur += 1; _cur_lexeme = lex_less; } break; case '!': if (*(cur+1) == '=') { cur += 2; _cur_lexeme = lex_not_equal; } else { _cur_lexeme = lex_none; } break; case '=': cur += 1; _cur_lexeme = lex_equal; break; case '+': cur += 1; _cur_lexeme = lex_plus; break; case '-': cur += 1; _cur_lexeme = lex_minus; break; case '*': cur += 1; _cur_lexeme = lex_multiply; break; case '|': cur += 1; _cur_lexeme = lex_union; break; case '$': cur += 1; if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol)) { _cur_lexeme_contents.begin = cur; while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; if (cur[0] == ':' && PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // qname { cur++; // : while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; } _cur_lexeme_contents.end = cur; _cur_lexeme = lex_var_ref; } else { _cur_lexeme = lex_none; } break; case '(': cur += 1; _cur_lexeme = lex_open_brace; break; case ')': cur += 1; _cur_lexeme = lex_close_brace; break; case '[': cur += 1; _cur_lexeme = lex_open_square_brace; break; case ']': cur += 1; _cur_lexeme = lex_close_square_brace; break; case ',': cur += 1; _cur_lexeme = lex_comma; break; case '/': if (*(cur+1) == '/') { cur += 2; _cur_lexeme = lex_double_slash; } else { cur += 1; _cur_lexeme = lex_slash; } break; case '.': if (*(cur+1) == '.') { cur += 2; _cur_lexeme = lex_double_dot; } else if (PUGI__IS_CHARTYPEX(*(cur+1), ctx_digit)) { _cur_lexeme_contents.begin = cur; // . ++cur; while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; _cur_lexeme_contents.end = cur; _cur_lexeme = lex_number; } else { cur += 1; _cur_lexeme = lex_dot; } break; case '@': cur += 1; _cur_lexeme = lex_axis_attribute; break; case '"': case '\'': { char_t terminator = *cur; ++cur; _cur_lexeme_contents.begin = cur; while (*cur && *cur != terminator) cur++; _cur_lexeme_contents.end = cur; if (!*cur) _cur_lexeme = lex_none; else { cur += 1; _cur_lexeme = lex_quoted_string; } break; } case ':': if (*(cur+1) == ':') { cur += 2; _cur_lexeme = lex_double_colon; } else { _cur_lexeme = lex_none; } break; default: if (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) { _cur_lexeme_contents.begin = cur; while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; if (*cur == '.') { cur++; while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; } _cur_lexeme_contents.end = cur; _cur_lexeme = lex_number; } else if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol)) { _cur_lexeme_contents.begin = cur; while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; if (cur[0] == ':') { if (cur[1] == '*') // namespace test ncname:* { cur += 2; // :* } else if (PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // namespace test qname { cur++; // : while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; } } _cur_lexeme_contents.end = cur; _cur_lexeme = lex_string; } else { _cur_lexeme = lex_none; } } _cur = cur; } lexeme_t current() const { return _cur_lexeme; } const char_t* current_pos() const { return _cur_lexeme_pos; } const xpath_lexer_string& contents() const { assert(_cur_lexeme == lex_var_ref || _cur_lexeme == lex_number || _cur_lexeme == lex_string || _cur_lexeme == lex_quoted_string); return _cur_lexeme_contents; } }; enum ast_type_t { ast_unknown, ast_op_or, // left or right ast_op_and, // left and right ast_op_equal, // left = right ast_op_not_equal, // left != right ast_op_less, // left < right ast_op_greater, // left > right ast_op_less_or_equal, // left <= right ast_op_greater_or_equal, // left >= right ast_op_add, // left + right ast_op_subtract, // left - right ast_op_multiply, // left * right ast_op_divide, // left / right ast_op_mod, // left % right ast_op_negate, // left - right ast_op_union, // left | right ast_predicate, // apply predicate to set; next points to next predicate ast_filter, // select * from left where right ast_string_constant, // string constant ast_number_constant, // number constant ast_variable, // variable ast_func_last, // last() ast_func_position, // position() ast_func_count, // count(left) ast_func_id, // id(left) ast_func_local_name_0, // local-name() ast_func_local_name_1, // local-name(left) ast_func_namespace_uri_0, // namespace-uri() ast_func_namespace_uri_1, // namespace-uri(left) ast_func_name_0, // name() ast_func_name_1, // name(left) ast_func_string_0, // string() ast_func_string_1, // string(left) ast_func_concat, // concat(left, right, siblings) ast_func_starts_with, // starts_with(left, right) ast_func_contains, // contains(left, right) ast_func_substring_before, // substring-before(left, right) ast_func_substring_after, // substring-after(left, right) ast_func_substring_2, // substring(left, right) ast_func_substring_3, // substring(left, right, third) ast_func_string_length_0, // string-length() ast_func_string_length_1, // string-length(left) ast_func_normalize_space_0, // normalize-space() ast_func_normalize_space_1, // normalize-space(left) ast_func_translate, // translate(left, right, third) ast_func_boolean, // boolean(left) ast_func_not, // not(left) ast_func_true, // true() ast_func_false, // false() ast_func_lang, // lang(left) ast_func_number_0, // number() ast_func_number_1, // number(left) ast_func_sum, // sum(left) ast_func_floor, // floor(left) ast_func_ceiling, // ceiling(left) ast_func_round, // round(left) ast_step, // process set left with step ast_step_root, // select root node ast_opt_translate_table, // translate(left, right, third) where right/third are constants ast_opt_compare_attribute // @name = 'string' }; enum axis_t { axis_ancestor, axis_ancestor_or_self, axis_attribute, axis_child, axis_descendant, axis_descendant_or_self, axis_following, axis_following_sibling, axis_namespace, axis_parent, axis_preceding, axis_preceding_sibling, axis_self }; enum nodetest_t { nodetest_none, nodetest_name, nodetest_type_node, nodetest_type_comment, nodetest_type_pi, nodetest_type_text, nodetest_pi, nodetest_all, nodetest_all_in_namespace }; enum predicate_t { predicate_default, predicate_posinv, predicate_constant, predicate_constant_one }; enum nodeset_eval_t { nodeset_eval_all, nodeset_eval_any, nodeset_eval_first }; template struct axis_to_type { static const axis_t axis; }; template const axis_t axis_to_type::axis = N; class xpath_ast_node { private: // node type char _type; char _rettype; // for ast_step char _axis; // for ast_step/ast_predicate/ast_filter char _test; // tree node structure xpath_ast_node* _left; xpath_ast_node* _right; xpath_ast_node* _next; union { // value for ast_string_constant const char_t* string; // value for ast_number_constant double number; // variable for ast_variable xpath_variable* variable; // node test for ast_step (node name/namespace/node type/pi target) const char_t* nodetest; // table for ast_opt_translate_table const unsigned char* table; } _data; xpath_ast_node(const xpath_ast_node&); xpath_ast_node& operator=(const xpath_ast_node&); template static bool compare_eq(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp) { xpath_value_type lt = lhs->rettype(), rt = rhs->rettype(); if (lt != xpath_type_node_set && rt != xpath_type_node_set) { if (lt == xpath_type_boolean || rt == xpath_type_boolean) return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack)); else if (lt == xpath_type_number || rt == xpath_type_number) return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack)); else if (lt == xpath_type_string || rt == xpath_type_string) { xpath_allocator_capture cr(stack.result); xpath_string ls = lhs->eval_string(c, stack); xpath_string rs = rhs->eval_string(c, stack); return comp(ls, rs); } } else if (lt == xpath_type_node_set && rt == xpath_type_node_set) { xpath_allocator_capture cr(stack.result); xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all); xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) { xpath_allocator_capture cri(stack.result); if (comp(string_value(*li, stack.result), string_value(*ri, stack.result))) return true; } return false; } else { if (lt == xpath_type_node_set) { swap(lhs, rhs); swap(lt, rt); } if (lt == xpath_type_boolean) return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack)); else if (lt == xpath_type_number) { xpath_allocator_capture cr(stack.result); double l = lhs->eval_number(c, stack); xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) { xpath_allocator_capture cri(stack.result); if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) return true; } return false; } else if (lt == xpath_type_string) { xpath_allocator_capture cr(stack.result); xpath_string l = lhs->eval_string(c, stack); xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) { xpath_allocator_capture cri(stack.result); if (comp(l, string_value(*ri, stack.result))) return true; } return false; } } assert(!"Wrong types"); return false; } static bool eval_once(xpath_node_set::type_t type, nodeset_eval_t eval) { return type == xpath_node_set::type_sorted ? eval != nodeset_eval_all : eval == nodeset_eval_any; } template static bool compare_rel(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp) { xpath_value_type lt = lhs->rettype(), rt = rhs->rettype(); if (lt != xpath_type_node_set && rt != xpath_type_node_set) return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack)); else if (lt == xpath_type_node_set && rt == xpath_type_node_set) { xpath_allocator_capture cr(stack.result); xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all); xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) { xpath_allocator_capture cri(stack.result); double l = convert_string_to_number(string_value(*li, stack.result).c_str()); for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) { xpath_allocator_capture crii(stack.result); if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) return true; } } return false; } else if (lt != xpath_type_node_set && rt == xpath_type_node_set) { xpath_allocator_capture cr(stack.result); double l = lhs->eval_number(c, stack); xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) { xpath_allocator_capture cri(stack.result); if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) return true; } return false; } else if (lt == xpath_type_node_set && rt != xpath_type_node_set) { xpath_allocator_capture cr(stack.result); xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all); double r = rhs->eval_number(c, stack); for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) { xpath_allocator_capture cri(stack.result); if (comp(convert_string_to_number(string_value(*li, stack.result).c_str()), r)) return true; } return false; } else { assert(!"Wrong types"); return false; } } static void apply_predicate_boolean(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once) { assert(ns.size() >= first); assert(expr->rettype() != xpath_type_number); size_t i = 1; size_t size = ns.size() - first; xpath_node* last = ns.begin() + first; // remove_if... or well, sort of for (xpath_node* it = last; it != ns.end(); ++it, ++i) { xpath_context c(*it, i, size); if (expr->eval_boolean(c, stack)) { *last++ = *it; if (once) break; } } ns.truncate(last); } static void apply_predicate_number(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once) { assert(ns.size() >= first); assert(expr->rettype() == xpath_type_number); size_t i = 1; size_t size = ns.size() - first; xpath_node* last = ns.begin() + first; // remove_if... or well, sort of for (xpath_node* it = last; it != ns.end(); ++it, ++i) { xpath_context c(*it, i, size); if (expr->eval_number(c, stack) == i) { *last++ = *it; if (once) break; } } ns.truncate(last); } static void apply_predicate_number_const(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack) { assert(ns.size() >= first); assert(expr->rettype() == xpath_type_number); size_t size = ns.size() - first; xpath_node* last = ns.begin() + first; xpath_context c(xpath_node(), 1, size); double er = expr->eval_number(c, stack); if (er >= 1.0 && er <= size) { size_t eri = static_cast(er); if (er == eri) { xpath_node r = last[eri - 1]; *last++ = r; } } ns.truncate(last); } void apply_predicate(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, bool once) { if (ns.size() == first) return; assert(_type == ast_filter || _type == ast_predicate); if (_test == predicate_constant || _test == predicate_constant_one) apply_predicate_number_const(ns, first, _right, stack); else if (_right->rettype() == xpath_type_number) apply_predicate_number(ns, first, _right, stack, once); else apply_predicate_boolean(ns, first, _right, stack, once); } void apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, nodeset_eval_t eval) { if (ns.size() == first) return; bool last_once = eval_once(ns.type(), eval); for (xpath_ast_node* pred = _right; pred; pred = pred->_next) pred->apply_predicate(ns, first, stack, !pred->_next && last_once); } bool step_push(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* parent, xpath_allocator* alloc) { assert(a); const char_t* name = a->name ? a->name + 0 : PUGIXML_TEXT(""); switch (_test) { case nodetest_name: if (strequal(name, _data.nodetest) && is_xpath_attribute(name)) { ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc); return true; } break; case nodetest_type_node: case nodetest_all: if (is_xpath_attribute(name)) { ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc); return true; } break; case nodetest_all_in_namespace: if (starts_with(name, _data.nodetest) && is_xpath_attribute(name)) { ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc); return true; } break; default: ; } return false; } bool step_push(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc) { assert(n); xml_node_type type = PUGI__NODETYPE(n); switch (_test) { case nodetest_name: if (type == node_element && n->name && strequal(n->name, _data.nodetest)) { ns.push_back(xml_node(n), alloc); return true; } break; case nodetest_type_node: ns.push_back(xml_node(n), alloc); return true; case nodetest_type_comment: if (type == node_comment) { ns.push_back(xml_node(n), alloc); return true; } break; case nodetest_type_text: if (type == node_pcdata || type == node_cdata) { ns.push_back(xml_node(n), alloc); return true; } break; case nodetest_type_pi: if (type == node_pi) { ns.push_back(xml_node(n), alloc); return true; } break; case nodetest_pi: if (type == node_pi && n->name && strequal(n->name, _data.nodetest)) { ns.push_back(xml_node(n), alloc); return true; } break; case nodetest_all: if (type == node_element) { ns.push_back(xml_node(n), alloc); return true; } break; case nodetest_all_in_namespace: if (type == node_element && n->name && starts_with(n->name, _data.nodetest)) { ns.push_back(xml_node(n), alloc); return true; } break; default: assert(!"Unknown axis"); } return false; } template void step_fill(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc, bool once, T) { const axis_t axis = T::axis; switch (axis) { case axis_attribute: { for (xml_attribute_struct* a = n->first_attribute; a; a = a->next_attribute) if (step_push(ns, a, n, alloc) & once) return; break; } case axis_child: { for (xml_node_struct* c = n->first_child; c; c = c->next_sibling) if (step_push(ns, c, alloc) & once) return; break; } case axis_descendant: case axis_descendant_or_self: { if (axis == axis_descendant_or_self) if (step_push(ns, n, alloc) & once) return; xml_node_struct* cur = n->first_child; while (cur) { if (step_push(ns, cur, alloc) & once) return; if (cur->first_child) cur = cur->first_child; else { while (!cur->next_sibling) { cur = cur->parent; if (cur == n) return; } cur = cur->next_sibling; } } break; } case axis_following_sibling: { for (xml_node_struct* c = n->next_sibling; c; c = c->next_sibling) if (step_push(ns, c, alloc) & once) return; break; } case axis_preceding_sibling: { for (xml_node_struct* c = n->prev_sibling_c; c->next_sibling; c = c->prev_sibling_c) if (step_push(ns, c, alloc) & once) return; break; } case axis_following: { xml_node_struct* cur = n; // exit from this node so that we don't include descendants while (!cur->next_sibling) { cur = cur->parent; if (!cur) return; } cur = cur->next_sibling; while (cur) { if (step_push(ns, cur, alloc) & once) return; if (cur->first_child) cur = cur->first_child; else { while (!cur->next_sibling) { cur = cur->parent; if (!cur) return; } cur = cur->next_sibling; } } break; } case axis_preceding: { xml_node_struct* cur = n; // exit from this node so that we don't include descendants while (!cur->prev_sibling_c->next_sibling) { cur = cur->parent; if (!cur) return; } cur = cur->prev_sibling_c; while (cur) { if (cur->first_child) cur = cur->first_child->prev_sibling_c; else { // leaf node, can't be ancestor if (step_push(ns, cur, alloc) & once) return; while (!cur->prev_sibling_c->next_sibling) { cur = cur->parent; if (!cur) return; if (!node_is_ancestor(cur, n)) if (step_push(ns, cur, alloc) & once) return; } cur = cur->prev_sibling_c; } } break; } case axis_ancestor: case axis_ancestor_or_self: { if (axis == axis_ancestor_or_self) if (step_push(ns, n, alloc) & once) return; xml_node_struct* cur = n->parent; while (cur) { if (step_push(ns, cur, alloc) & once) return; cur = cur->parent; } break; } case axis_self: { step_push(ns, n, alloc); break; } case axis_parent: { if (n->parent) step_push(ns, n->parent, alloc); break; } default: assert(!"Unimplemented axis"); } } template void step_fill(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* p, xpath_allocator* alloc, bool once, T v) { const axis_t axis = T::axis; switch (axis) { case axis_ancestor: case axis_ancestor_or_self: { if (axis == axis_ancestor_or_self && _test == nodetest_type_node) // reject attributes based on principal node type test if (step_push(ns, a, p, alloc) & once) return; xml_node_struct* cur = p; while (cur) { if (step_push(ns, cur, alloc) & once) return; cur = cur->parent; } break; } case axis_descendant_or_self: case axis_self: { if (_test == nodetest_type_node) // reject attributes based on principal node type test step_push(ns, a, p, alloc); break; } case axis_following: { xml_node_struct* cur = p; while (cur) { if (cur->first_child) cur = cur->first_child; else { while (!cur->next_sibling) { cur = cur->parent; if (!cur) return; } cur = cur->next_sibling; } if (step_push(ns, cur, alloc) & once) return; } break; } case axis_parent: { step_push(ns, p, alloc); break; } case axis_preceding: { // preceding:: axis does not include attribute nodes and attribute ancestors (they are the same as parent's ancestors), so we can reuse node preceding step_fill(ns, p, alloc, once, v); break; } default: assert(!"Unimplemented axis"); } } template void step_fill(xpath_node_set_raw& ns, const xpath_node& xn, xpath_allocator* alloc, bool once, T v) { const axis_t axis = T::axis; const bool axis_has_attributes = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_descendant_or_self || axis == axis_following || axis == axis_parent || axis == axis_preceding || axis == axis_self); if (xn.node()) step_fill(ns, xn.node().internal_object(), alloc, once, v); else if (axis_has_attributes && xn.attribute() && xn.parent()) step_fill(ns, xn.attribute().internal_object(), xn.parent().internal_object(), alloc, once, v); } template xpath_node_set_raw step_do(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval, T v) { const axis_t axis = T::axis; const bool axis_reverse = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling); const xpath_node_set::type_t axis_type = axis_reverse ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted; bool once = (axis == axis_attribute && _test == nodetest_name) || (!_right && eval_once(axis_type, eval)) || (_right && !_right->_next && _right->_test == predicate_constant_one); xpath_node_set_raw ns; ns.set_type(axis_type); if (_left) { xpath_node_set_raw s = _left->eval_node_set(c, stack, nodeset_eval_all); // self axis preserves the original order if (axis == axis_self) ns.set_type(s.type()); for (const xpath_node* it = s.begin(); it != s.end(); ++it) { size_t size = ns.size(); // in general, all axes generate elements in a particular order, but there is no order guarantee if axis is applied to two nodes if (axis != axis_self && size != 0) ns.set_type(xpath_node_set::type_unsorted); step_fill(ns, *it, stack.result, once, v); if (_right) apply_predicates(ns, size, stack, eval); } } else { step_fill(ns, c.n, stack.result, once, v); if (_right) apply_predicates(ns, 0, stack, eval); } // child, attribute and self axes always generate unique set of nodes // for other axis, if the set stayed sorted, it stayed unique because the traversal algorithms do not visit the same node twice if (axis != axis_child && axis != axis_attribute && axis != axis_self && ns.type() == xpath_node_set::type_unsorted) ns.remove_duplicates(); return ns; } public: xpath_ast_node(ast_type_t type, xpath_value_type rettype_, const char_t* value): _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) { assert(type == ast_string_constant); _data.string = value; } xpath_ast_node(ast_type_t type, xpath_value_type rettype_, double value): _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) { assert(type == ast_number_constant); _data.number = value; } xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_variable* value): _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) { assert(type == ast_variable); _data.variable = value; } xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_ast_node* left = 0, xpath_ast_node* right = 0): _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(left), _right(right), _next(0) { } xpath_ast_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents): _type(static_cast(type)), _rettype(xpath_type_node_set), _axis(static_cast(axis)), _test(static_cast(test)), _left(left), _right(0), _next(0) { assert(type == ast_step); _data.nodetest = contents; } xpath_ast_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, predicate_t test): _type(static_cast(type)), _rettype(xpath_type_node_set), _axis(0), _test(static_cast(test)), _left(left), _right(right), _next(0) { assert(type == ast_filter || type == ast_predicate); } void set_next(xpath_ast_node* value) { _next = value; } void set_right(xpath_ast_node* value) { _right = value; } bool eval_boolean(const xpath_context& c, const xpath_stack& stack) { switch (_type) { case ast_op_or: return _left->eval_boolean(c, stack) || _right->eval_boolean(c, stack); case ast_op_and: return _left->eval_boolean(c, stack) && _right->eval_boolean(c, stack); case ast_op_equal: return compare_eq(_left, _right, c, stack, equal_to()); case ast_op_not_equal: return compare_eq(_left, _right, c, stack, not_equal_to()); case ast_op_less: return compare_rel(_left, _right, c, stack, less()); case ast_op_greater: return compare_rel(_right, _left, c, stack, less()); case ast_op_less_or_equal: return compare_rel(_left, _right, c, stack, less_equal()); case ast_op_greater_or_equal: return compare_rel(_right, _left, c, stack, less_equal()); case ast_func_starts_with: { xpath_allocator_capture cr(stack.result); xpath_string lr = _left->eval_string(c, stack); xpath_string rr = _right->eval_string(c, stack); return starts_with(lr.c_str(), rr.c_str()); } case ast_func_contains: { xpath_allocator_capture cr(stack.result); xpath_string lr = _left->eval_string(c, stack); xpath_string rr = _right->eval_string(c, stack); return find_substring(lr.c_str(), rr.c_str()) != 0; } case ast_func_boolean: return _left->eval_boolean(c, stack); case ast_func_not: return !_left->eval_boolean(c, stack); case ast_func_true: return true; case ast_func_false: return false; case ast_func_lang: { if (c.n.attribute()) return false; xpath_allocator_capture cr(stack.result); xpath_string lang = _left->eval_string(c, stack); for (xml_node n = c.n.node(); n; n = n.parent()) { xml_attribute a = n.attribute(PUGIXML_TEXT("xml:lang")); if (a) { const char_t* value = a.value(); // strnicmp / strncasecmp is not portable for (const char_t* lit = lang.c_str(); *lit; ++lit) { if (tolower_ascii(*lit) != tolower_ascii(*value)) return false; ++value; } return *value == 0 || *value == '-'; } } return false; } case ast_opt_compare_attribute: { const char_t* value = (_right->_type == ast_string_constant) ? _right->_data.string : _right->_data.variable->get_string(); xml_attribute attr = c.n.node().attribute(_left->_data.nodetest); return attr && strequal(attr.value(), value) && is_xpath_attribute(attr.name()); } case ast_variable: { assert(_rettype == _data.variable->type()); if (_rettype == xpath_type_boolean) return _data.variable->get_boolean(); // fallthrough 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, nodeset_eval_any).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 static_cast(c.size); case ast_func_position: return static_cast(c.position); case ast_func_count: { xpath_allocator_capture cr(stack.result); return static_cast(_left->eval_node_set(c, stack, nodeset_eval_all).size()); } case ast_func_string_length_0: { xpath_allocator_capture cr(stack.result); return static_cast(string_value(c.n, stack.result).length()); } case ast_func_string_length_1: { xpath_allocator_capture cr(stack.result); return static_cast(_left->eval_string(c, stack).length()); } case ast_func_number_0: { xpath_allocator_capture cr(stack.result); return convert_string_to_number(string_value(c.n, stack.result).c_str()); } case ast_func_number_1: return _left->eval_number(c, stack); case ast_func_sum: { xpath_allocator_capture cr(stack.result); double r = 0; xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_all); for (const xpath_node* it = ns.begin(); it != ns.end(); ++it) { xpath_allocator_capture cri(stack.result); r += convert_string_to_number(string_value(*it, stack.result).c_str()); } return r; } case ast_func_floor: { double r = _left->eval_number(c, stack); return r == r ? floor(r) : r; } case ast_func_ceiling: { double r = _left->eval_number(c, stack); return r == r ? ceil(r) : r; } case ast_func_round: return round_nearest_nzero(_left->eval_number(c, stack)); case ast_variable: { assert(_rettype == _data.variable->type()); if (_rettype == xpath_type_number) return _data.variable->get_number(); // fallthrough 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::from_heap_preallocated(result, ri); } xpath_string eval_string(const xpath_context& c, const xpath_stack& stack) { switch (_type) { case ast_string_constant: return xpath_string::from_const(_data.string); case ast_func_local_name_0: { xpath_node na = c.n; return xpath_string::from_const(local_name(na)); } case ast_func_local_name_1: { xpath_allocator_capture cr(stack.result); xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); xpath_node na = ns.first(); return xpath_string::from_const(local_name(na)); } case ast_func_name_0: { xpath_node na = c.n; return xpath_string::from_const(qualified_name(na)); } case ast_func_name_1: { xpath_allocator_capture cr(stack.result); xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); xpath_node na = ns.first(); return xpath_string::from_const(qualified_name(na)); } case ast_func_namespace_uri_0: { xpath_node na = c.n; return xpath_string::from_const(namespace_uri(na)); } case ast_func_namespace_uri_1: { xpath_allocator_capture cr(stack.result); xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); xpath_node na = ns.first(); return xpath_string::from_const(namespace_uri(na)); } case ast_func_string_0: return string_value(c.n, stack.result); case ast_func_string_1: return _left->eval_string(c, stack); case ast_func_concat: return eval_string_concat(c, stack); case ast_func_substring_before: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_string s = _left->eval_string(c, swapped_stack); xpath_string p = _right->eval_string(c, swapped_stack); const char_t* pos = find_substring(s.c_str(), p.c_str()); return pos ? xpath_string::from_heap(s.c_str(), pos, stack.result) : xpath_string(); } case ast_func_substring_after: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_string s = _left->eval_string(c, swapped_stack); xpath_string p = _right->eval_string(c, swapped_stack); const char_t* pos = find_substring(s.c_str(), p.c_str()); if (!pos) return xpath_string(); const char_t* rbegin = pos + p.length(); const char_t* rend = s.c_str() + s.length(); return s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin); } case ast_func_substring_2: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_string s = _left->eval_string(c, swapped_stack); size_t s_length = s.length(); double first = round_nearest(_right->eval_number(c, stack)); if (is_nan(first)) return xpath_string(); // NaN else if (first >= s_length + 1) return xpath_string(); size_t pos = first < 1 ? 1 : static_cast(first); assert(1 <= pos && pos <= s_length + 1); const char_t* rbegin = s.c_str() + (pos - 1); const char_t* rend = s.c_str() + s.length(); return s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin); } case ast_func_substring_3: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_string s = _left->eval_string(c, swapped_stack); size_t s_length = s.length(); double first = round_nearest(_right->eval_number(c, stack)); double last = first + round_nearest(_right->_next->eval_number(c, stack)); if (is_nan(first) || is_nan(last)) return xpath_string(); else if (first >= s_length + 1) return xpath_string(); else if (first >= last) return xpath_string(); else if (last < 1) return xpath_string(); size_t pos = first < 1 ? 1 : static_cast(first); size_t end = last >= s_length + 1 ? s_length + 1 : static_cast(last); assert(1 <= pos && pos <= end && end <= s_length + 1); const char_t* rbegin = s.c_str() + (pos - 1); const char_t* rend = s.c_str() + (end - 1); return (end == s_length + 1 && !s.uses_heap()) ? xpath_string::from_const(rbegin) : xpath_string::from_heap(rbegin, rend, stack.result); } case ast_func_normalize_space_0: { xpath_string s = string_value(c.n, stack.result); char_t* begin = s.data(stack.result); char_t* end = normalize_space(begin); return xpath_string::from_heap_preallocated(begin, end); } case ast_func_normalize_space_1: { xpath_string s = _left->eval_string(c, stack); char_t* begin = s.data(stack.result); char_t* end = normalize_space(begin); return xpath_string::from_heap_preallocated(begin, end); } case ast_func_translate: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_string s = _left->eval_string(c, stack); xpath_string from = _right->eval_string(c, swapped_stack); xpath_string to = _right->_next->eval_string(c, swapped_stack); char_t* begin = s.data(stack.result); char_t* end = translate(begin, from.c_str(), to.c_str(), to.length()); return xpath_string::from_heap_preallocated(begin, end); } case ast_opt_translate_table: { xpath_string s = _left->eval_string(c, stack); char_t* begin = s.data(stack.result); char_t* end = translate_table(begin, _data.table); return xpath_string::from_heap_preallocated(begin, end); } case ast_variable: { assert(_rettype == _data.variable->type()); if (_rettype == xpath_type_string) return xpath_string::from_const(_data.variable->get_string()); // fallthrough to type conversion } default: { switch (_rettype) { case xpath_type_boolean: return xpath_string::from_const(eval_boolean(c, stack) ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false")); case xpath_type_number: return convert_number_to_string(eval_number(c, stack), stack.result); case xpath_type_node_set: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_node_set_raw ns = eval_node_set(c, swapped_stack, nodeset_eval_first); return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result); } default: assert(!"Wrong expression for return type string"); return xpath_string(); } } } } xpath_node_set_raw eval_node_set(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval) { switch (_type) { case ast_op_union: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_node_set_raw ls = _left->eval_node_set(c, swapped_stack, eval); xpath_node_set_raw rs = _right->eval_node_set(c, stack, eval); // 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: { xpath_node_set_raw set = _left->eval_node_set(c, stack, _test == predicate_constant_one ? nodeset_eval_first : nodeset_eval_all); // either expression is a number or it contains position() call; sort by document order if (_test != predicate_posinv) set.sort_do(); bool once = eval_once(set.type(), eval); apply_predicate(set, 0, stack, once); return set; } case ast_func_id: return xpath_node_set_raw(); case ast_step: { switch (_axis) { case axis_ancestor: return step_do(c, stack, eval, axis_to_type()); case axis_ancestor_or_self: return step_do(c, stack, eval, axis_to_type()); case axis_attribute: return step_do(c, stack, eval, axis_to_type()); case axis_child: return step_do(c, stack, eval, axis_to_type()); case axis_descendant: return step_do(c, stack, eval, axis_to_type()); case axis_descendant_or_self: return step_do(c, stack, eval, axis_to_type()); case axis_following: return step_do(c, stack, eval, axis_to_type()); case axis_following_sibling: return step_do(c, stack, eval, axis_to_type()); case axis_namespace: // namespaced axis is not supported return xpath_node_set_raw(); case axis_parent: return step_do(c, stack, eval, axis_to_type()); case axis_preceding: return step_do(c, stack, eval, axis_to_type()); case axis_preceding_sibling: return step_do(c, stack, eval, axis_to_type()); case axis_self: return step_do(c, stack, eval, axis_to_type()); default: assert(!"Unknown axis"); return xpath_node_set_raw(); } } case ast_step_root: { assert(!_right); // root step can't have any predicates xpath_node_set_raw ns; ns.set_type(xpath_node_set::type_sorted); if (c.n.node()) ns.push_back(c.n.node().root(), stack.result); else if (c.n.attribute()) ns.push_back(c.n.parent().root(), stack.result); return ns; } case ast_variable: { assert(_rettype == _data.variable->type()); if (_rettype == xpath_type_node_set) { const xpath_node_set& s = _data.variable->get_node_set(); xpath_node_set_raw ns; ns.set_type(s.type()); ns.append(s.begin(), s.end(), stack.result); return ns; } // fallthrough to type conversion } default: assert(!"Wrong expression for return type node set"); return xpath_node_set_raw(); } } void optimize(xpath_allocator* alloc) { if (_left) _left->optimize(alloc); if (_right) _right->optimize(alloc); if (_next) _next->optimize(alloc); optimize_self(alloc); } void optimize_self(xpath_allocator* alloc) { // Rewrite [position()=expr] with [expr] // Note that this step has to go before classification to recognize [position()=1] if ((_type == ast_filter || _type == ast_predicate) && _right->_type == ast_op_equal && _right->_left->_type == ast_func_position && _right->_right->_rettype == xpath_type_number) { _right = _right->_right; } // Classify filter/predicate ops to perform various optimizations during evaluation if (_type == ast_filter || _type == ast_predicate) { assert(_test == predicate_default); if (_right->_type == ast_number_constant && _right->_data.number == 1.0) _test = predicate_constant_one; else if (_right->_rettype == xpath_type_number && (_right->_type == ast_number_constant || _right->_type == ast_variable || _right->_type == ast_func_last)) _test = predicate_constant; else if (_right->_rettype != xpath_type_number && _right->is_posinv_expr()) _test = predicate_posinv; } // Rewrite descendant-or-self::node()/child::foo with descendant::foo // The former is a full form of //foo, the latter is much faster since it executes the node test immediately // Do a similar kind of rewrite for self/descendant/descendant-or-self axes // Note that we only rewrite positionally invariant steps (//foo[1] != /descendant::foo[1]) if (_type == ast_step && (_axis == axis_child || _axis == axis_self || _axis == axis_descendant || _axis == axis_descendant_or_self) && _left && _left->_type == ast_step && _left->_axis == axis_descendant_or_self && _left->_test == nodetest_type_node && !_left->_right && is_posinv_step()) { if (_axis == axis_child || _axis == axis_descendant) _axis = axis_descendant; else _axis = axis_descendant_or_self; _left = _left->_left; } // Use optimized lookup table implementation for translate() with constant arguments if (_type == ast_func_translate && _right->_type == ast_string_constant && _right->_next->_type == ast_string_constant) { unsigned char* table = translate_table_generate(alloc, _right->_data.string, _right->_next->_data.string); if (table) { _type = ast_opt_translate_table; _data.table = table; } } // Use optimized path for @attr = 'value' or @attr = $value if (_type == ast_op_equal && _left->_type == ast_step && _left->_axis == axis_attribute && _left->_test == nodetest_name && !_left->_left && !_left->_right && (_right->_type == ast_string_constant || (_right->_type == ast_variable && _right->_rettype == xpath_type_string))) { _type = ast_opt_compare_attribute; } } bool is_posinv_expr() const { switch (_type) { case ast_func_position: case ast_func_last: return false; case ast_string_constant: case ast_number_constant: case ast_variable: return true; case ast_step: case ast_step_root: return true; case ast_predicate: case ast_filter: return true; default: if (_left && !_left->is_posinv_expr()) return false; for (xpath_ast_node* n = _right; n; n = n->_next) if (!n->is_posinv_expr()) return false; return true; } } bool is_posinv_step() const { assert(_type == ast_step); for (xpath_ast_node* n = _right; n; n = n->_next) { assert(n->_type == ast_predicate); if (n->_test != predicate_posinv) return false; } return true; } xpath_value_type rettype() const { return static_cast(_rettype); } }; struct xpath_parser { xpath_allocator* _alloc; xpath_lexer _lexer; const char_t* _query; xpath_variable_set* _variables; xpath_parse_result* _result; char_t _scratch[32]; #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(); assert(c); // workaround for clang static analysis 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_boolean, 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_number, 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; default: 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; default: break; } specified = false; return axis_child; } nodetest_t parse_node_test_type(const xpath_lexer_string& name) { switch (name.begin[0]) { case 'c': if (name == PUGIXML_TEXT("comment")) return nodetest_type_comment; break; case 'n': if (name == PUGIXML_TEXT("node")) return nodetest_type_node; break; case 'p': if (name == PUGIXML_TEXT("processing-instruction")) return nodetest_type_pi; break; case 't': if (name == PUGIXML_TEXT("text")) return nodetest_type_text; break; default: break; } return nodetest_none; } // PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall xpath_ast_node* parse_primary_expression() { switch (_lexer.current()) { case lex_var_ref: { xpath_lexer_string name = _lexer.contents(); if (!_variables) throw_error("Unknown variable: variable set is not provided"); xpath_variable* var = 0; if (!get_variable_scratch(_scratch, _variables, name.begin, name.end, &var)) throw_error_oom(); 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_scratch(_scratch, _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"); n = new (alloc_node()) xpath_ast_node(ast_filter, n, expr, predicate_default); 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, 0, expr, predicate_default); 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 // UnionExpr ::= PathExpr | UnionExpr '|' PathExpr // UnaryExpr ::= UnionExpr | '-' UnaryExpr xpath_ast_node* parse_path_or_unary_expression() { // Clarification. // PathExpr begins with either LocationPath or FilterExpr. // FilterExpr begins with PrimaryExpr // PrimaryExpr begins with '$' in case of it being a variable reference, // '(' in case of it being an expression, string literal, number constant or // function call. if (_lexer.current() == lex_var_ref || _lexer.current() == lex_open_brace || _lexer.current() == lex_quoted_string || _lexer.current() == lex_number || _lexer.current() == lex_string) { if (_lexer.current() == lex_string) { // This is either a function call, or not - if not, we shall proceed with location path const char_t* state = _lexer.state(); while (PUGI__IS_CHARTYPE(*state, ct_space)) ++state; if (*state != '(') return parse_location_path(); // This looks like a function call; however this still can be a node-test. Check it. if (parse_node_test_type(_lexer.contents()) != nodetest_none) return parse_location_path(); } xpath_ast_node* n = parse_filter_expression(); if (_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 if (_lexer.current() == lex_minus) { _lexer.next(); // precedence 7+ - only parses union expressions xpath_ast_node* expr = parse_expression_rec(parse_path_or_unary_expression(), 7); return new (alloc_node()) xpath_ast_node(ast_op_negate, xpath_type_number, expr); } else return parse_location_path(); } struct binary_op_t { ast_type_t asttype; xpath_value_type rettype; int precedence; binary_op_t(): asttype(ast_unknown), rettype(xpath_type_none), precedence(0) { } binary_op_t(ast_type_t asttype_, xpath_value_type rettype_, int precedence_): asttype(asttype_), rettype(rettype_), precedence(precedence_) { } static binary_op_t parse(xpath_lexer& lexer) { switch (lexer.current()) { case lex_string: if (lexer.contents() == PUGIXML_TEXT("or")) return binary_op_t(ast_op_or, xpath_type_boolean, 1); else if (lexer.contents() == PUGIXML_TEXT("and")) return binary_op_t(ast_op_and, xpath_type_boolean, 2); else if (lexer.contents() == PUGIXML_TEXT("div")) return binary_op_t(ast_op_divide, xpath_type_number, 6); else if (lexer.contents() == PUGIXML_TEXT("mod")) return binary_op_t(ast_op_mod, xpath_type_number, 6); else return binary_op_t(); case lex_equal: return binary_op_t(ast_op_equal, xpath_type_boolean, 3); case lex_not_equal: return binary_op_t(ast_op_not_equal, xpath_type_boolean, 3); case lex_less: return binary_op_t(ast_op_less, xpath_type_boolean, 4); case lex_greater: return binary_op_t(ast_op_greater, xpath_type_boolean, 4); case lex_less_or_equal: return binary_op_t(ast_op_less_or_equal, xpath_type_boolean, 4); case lex_greater_or_equal: return binary_op_t(ast_op_greater_or_equal, xpath_type_boolean, 4); case lex_plus: return binary_op_t(ast_op_add, xpath_type_number, 5); case lex_minus: return binary_op_t(ast_op_subtract, xpath_type_number, 5); case lex_multiply: return binary_op_t(ast_op_multiply, xpath_type_number, 6); case lex_union: return binary_op_t(ast_op_union, xpath_type_node_set, 7); default: return binary_op_t(); } } }; xpath_ast_node* parse_expression_rec(xpath_ast_node* lhs, int limit) { binary_op_t op = binary_op_t::parse(_lexer); while (op.asttype != ast_unknown && op.precedence >= limit) { _lexer.next(); xpath_ast_node* rhs = parse_path_or_unary_expression(); binary_op_t nextop = binary_op_t::parse(_lexer); while (nextop.asttype != ast_unknown && nextop.precedence > op.precedence) { rhs = parse_expression_rec(rhs, nextop.precedence); nextop = binary_op_t::parse(_lexer); } if (op.asttype == ast_op_union && (lhs->rettype() != xpath_type_node_set || rhs->rettype() != xpath_type_node_set)) throw_error("Union operator has to be applied to node sets"); lhs = new (alloc_node()) xpath_ast_node(op.asttype, op.rettype, lhs, rhs); op = binary_op_t::parse(_lexer); } return lhs; } // Expr ::= OrExpr // OrExpr ::= AndExpr | OrExpr 'or' AndExpr // AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr // EqualityExpr ::= RelationalExpr // | EqualityExpr '=' RelationalExpr // | EqualityExpr '!=' RelationalExpr // RelationalExpr ::= AdditiveExpr // | RelationalExpr '<' AdditiveExpr // | RelationalExpr '>' AdditiveExpr // | RelationalExpr '<=' AdditiveExpr // | RelationalExpr '>=' AdditiveExpr // AdditiveExpr ::= MultiplicativeExpr // | AdditiveExpr '+' MultiplicativeExpr // | AdditiveExpr '-' MultiplicativeExpr // MultiplicativeExpr ::= UnaryExpr // | MultiplicativeExpr '*' UnaryExpr // | MultiplicativeExpr 'div' UnaryExpr // | MultiplicativeExpr 'mod' UnaryExpr xpath_ast_node* parse_expression() { return parse_expression_rec(parse_path_or_unary_expression(), 0); } 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 = xml_memory::allocate(sizeof(xpath_query_impl)); if (!memory) return 0; return new (memory) xpath_query_impl(); } static void destroy(xpath_query_impl* impl) { // free all allocated pages impl->alloc.release(); // free allocator memory (with the first page) xml_memory::deallocate(impl); } xpath_query_impl(): root(0), alloc(&block) { block.next = 0; block.capacity = sizeof(block.data); } xpath_ast_node* root; xpath_allocator alloc; xpath_memory_block block; }; PUGI__FN 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); } PUGI__FN impl::xpath_ast_node* evaluate_node_set_prepare(xpath_query_impl* impl) { if (!impl) return 0; if (impl->root->rettype() != xpath_type_node_set) { #ifdef PUGIXML_NO_EXCEPTIONS return 0; #else xpath_parse_result res; res.error = "Expression does not evaluate to node set"; throw xpath_exception(res); #endif } return impl->root; } PUGI__NS_END namespace pugi { #ifndef PUGIXML_NO_EXCEPTIONS PUGI__FN xpath_exception::xpath_exception(const xpath_parse_result& result_): _result(result_) { assert(_result.error); } PUGI__FN const char* xpath_exception::what() const throw() { return _result.error; } PUGI__FN const xpath_parse_result& xpath_exception::result() const { return _result; } #endif PUGI__FN xpath_node::xpath_node() { } PUGI__FN xpath_node::xpath_node(const xml_node& node_): _node(node_) { } PUGI__FN xpath_node::xpath_node(const xml_attribute& attribute_, const xml_node& parent_): _node(attribute_ ? parent_ : xml_node()), _attribute(attribute_) { } PUGI__FN xml_node xpath_node::node() const { return _attribute ? xml_node() : _node; } PUGI__FN xml_attribute xpath_node::attribute() const { return _attribute; } PUGI__FN xml_node xpath_node::parent() const { return _attribute ? _node : _node.parent(); } PUGI__FN static void unspecified_bool_xpath_node(xpath_node***) { } PUGI__FN xpath_node::operator xpath_node::unspecified_bool_type() const { return (_node || _attribute) ? unspecified_bool_xpath_node : 0; } PUGI__FN bool xpath_node::operator!() const { return !(_node || _attribute); } PUGI__FN bool xpath_node::operator==(const xpath_node& n) const { return _node == n._node && _attribute == n._attribute; } PUGI__FN bool xpath_node::operator!=(const xpath_node& n) const { return _node != n._node || _attribute != n._attribute; } #ifdef __BORLANDC__ PUGI__FN bool operator&&(const xpath_node& lhs, bool rhs) { return (bool)lhs && rhs; } PUGI__FN bool operator||(const xpath_node& lhs, bool rhs) { return (bool)lhs || rhs; } #endif PUGI__FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_, type_t type_) { assert(begin_ <= end_); size_t size_ = static_cast(end_ - begin_); if (size_ <= 1) { // deallocate old buffer if (_begin != &_storage) impl::xml_memory::deallocate(_begin); // use internal buffer if (begin_ != end_) _storage = *begin_; _begin = &_storage; _end = &_storage + size_; _type = type_; } else { // make heap copy xpath_node* storage = static_cast(impl::xml_memory::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) impl::xml_memory::deallocate(_begin); // finalize _begin = storage; _end = storage + size_; _type = type_; } } #if __cplusplus >= 201103 PUGI__FN void xpath_node_set::_move(xpath_node_set& rhs) { _type = rhs._type; _storage = rhs._storage; _begin = (rhs._begin == &rhs._storage) ? &_storage : rhs._begin; _end = _begin + (rhs._end - rhs._begin); rhs._type = type_unsorted; rhs._begin = &rhs._storage; rhs._end = rhs._begin; } #endif PUGI__FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(&_storage), _end(&_storage) { } PUGI__FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_unsorted), _begin(&_storage), _end(&_storage) { _assign(begin_, end_, type_); } PUGI__FN xpath_node_set::~xpath_node_set() { if (_begin != &_storage) impl::xml_memory::deallocate(_begin); } PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(type_unsorted), _begin(&_storage), _end(&_storage) { _assign(ns._begin, ns._end, ns._type); } PUGI__FN xpath_node_set& xpath_node_set::operator=(const xpath_node_set& ns) { if (this == &ns) return *this; _assign(ns._begin, ns._end, ns._type); return *this; } #if __cplusplus >= 201103 PUGI__FN xpath_node_set::xpath_node_set(xpath_node_set&& rhs): _type(type_unsorted), _begin(&_storage), _end(&_storage) { _move(rhs); } PUGI__FN xpath_node_set& xpath_node_set::operator=(xpath_node_set&& rhs) { if (this == &rhs) return *this; if (_begin != &_storage) impl::xml_memory::deallocate(_begin); _move(rhs); return *this; } #endif PUGI__FN xpath_node_set::type_t xpath_node_set::type() const { return _type; } PUGI__FN size_t xpath_node_set::size() const { return _end - _begin; } PUGI__FN bool xpath_node_set::empty() const { return _begin == _end; } PUGI__FN const xpath_node& xpath_node_set::operator[](size_t index) const { assert(index < size()); return _begin[index]; } PUGI__FN xpath_node_set::const_iterator xpath_node_set::begin() const { return _begin; } PUGI__FN xpath_node_set::const_iterator xpath_node_set::end() const { return _end; } PUGI__FN void xpath_node_set::sort(bool reverse) { _type = impl::xpath_sort(_begin, _end, _type, reverse); } PUGI__FN xpath_node xpath_node_set::first() const { return impl::xpath_first(_begin, _end, _type); } PUGI__FN xpath_parse_result::xpath_parse_result(): error("Internal error"), offset(0) { } PUGI__FN xpath_parse_result::operator bool() const { return error == 0; } PUGI__FN const char* xpath_parse_result::description() const { return error ? error : "No error"; } PUGI__FN xpath_variable::xpath_variable(xpath_value_type type_): _type(type_), _next(0) { } PUGI__FN const char_t* xpath_variable::name() const { switch (_type) { case xpath_type_node_set: return static_cast(this)->name; case xpath_type_number: return static_cast(this)->name; case xpath_type_string: return static_cast(this)->name; case xpath_type_boolean: return static_cast(this)->name; default: assert(!"Invalid variable type"); return 0; } } PUGI__FN xpath_value_type xpath_variable::type() const { return _type; } PUGI__FN bool xpath_variable::get_boolean() const { return (_type == xpath_type_boolean) ? static_cast(this)->value : false; } PUGI__FN double xpath_variable::get_number() const { return (_type == xpath_type_number) ? static_cast(this)->value : impl::gen_nan(); } PUGI__FN const char_t* xpath_variable::get_string() const { const char_t* value = (_type == xpath_type_string) ? static_cast(this)->value : 0; return value ? value : PUGIXML_TEXT(""); } PUGI__FN const xpath_node_set& xpath_variable::get_node_set() const { return (_type == xpath_type_node_set) ? static_cast(this)->value : impl::dummy_node_set; } PUGI__FN bool xpath_variable::set(bool value) { if (_type != xpath_type_boolean) return false; static_cast(this)->value = value; return true; } PUGI__FN bool xpath_variable::set(double value) { if (_type != xpath_type_number) return false; static_cast(this)->value = value; return true; } PUGI__FN bool xpath_variable::set(const char_t* value) { if (_type != xpath_type_string) return false; impl::xpath_variable_string* var = static_cast(this); // duplicate string size_t size = (impl::strlength(value) + 1) * sizeof(char_t); char_t* copy = static_cast(impl::xml_memory::allocate(size)); if (!copy) return false; memcpy(copy, value, size); // replace old string if (var->value) impl::xml_memory::deallocate(var->value); var->value = copy; return true; } PUGI__FN bool xpath_variable::set(const xpath_node_set& value) { if (_type != xpath_type_node_set) return false; static_cast(this)->value = value; return true; } PUGI__FN xpath_variable_set::xpath_variable_set() { for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) _data[i] = 0; } PUGI__FN xpath_variable_set::~xpath_variable_set() { for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) _destroy(_data[i]); } PUGI__FN xpath_variable_set::xpath_variable_set(const xpath_variable_set& rhs) { for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) _data[i] = 0; _assign(rhs); } PUGI__FN xpath_variable_set& xpath_variable_set::operator=(const xpath_variable_set& rhs) { if (this == &rhs) return *this; _assign(rhs); return *this; } #if __cplusplus >= 201103 PUGI__FN xpath_variable_set::xpath_variable_set(xpath_variable_set&& rhs) { for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) { _data[i] = rhs._data[i]; rhs._data[i] = 0; } } PUGI__FN xpath_variable_set& xpath_variable_set::operator=(xpath_variable_set&& rhs) { for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) { _destroy(_data[i]); _data[i] = rhs._data[i]; rhs._data[i] = 0; } return *this; } #endif PUGI__FN void xpath_variable_set::_assign(const xpath_variable_set& rhs) { xpath_variable_set temp; for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) if (rhs._data[i] && !_clone(rhs._data[i], &temp._data[i])) return; _swap(temp); } PUGI__FN void xpath_variable_set::_swap(xpath_variable_set& rhs) { for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) { xpath_variable* chain = _data[i]; _data[i] = rhs._data[i]; rhs._data[i] = chain; } } PUGI__FN xpath_variable* xpath_variable_set::_find(const char_t* name) const { const size_t hash_size = sizeof(_data) / sizeof(_data[0]); size_t hash = impl::hash_string(name) % hash_size; // look for existing variable for (xpath_variable* var = _data[hash]; var; var = var->_next) if (impl::strequal(var->name(), name)) return var; return 0; } PUGI__FN bool xpath_variable_set::_clone(xpath_variable* var, xpath_variable** out_result) { xpath_variable* last = 0; while (var) { // allocate storage for new variable xpath_variable* nvar = impl::new_xpath_variable(var->_type, var->name()); if (!nvar) return false; // link the variable to the result immediately to handle failures gracefully if (last) last->_next = nvar; else *out_result = nvar; last = nvar; // copy the value; this can fail due to out-of-memory conditions if (!impl::copy_xpath_variable(nvar, var)) return false; var = var->_next; } return true; } PUGI__FN void xpath_variable_set::_destroy(xpath_variable* var) { while (var) { xpath_variable* next = var->_next; impl::delete_xpath_variable(var->_type, var); var = next; } } PUGI__FN xpath_variable* xpath_variable_set::add(const char_t* name, xpath_value_type type) { const size_t hash_size = sizeof(_data) / sizeof(_data[0]); size_t hash = impl::hash_string(name) % hash_size; // look for existing variable for (xpath_variable* var = _data[hash]; var; var = var->_next) if (impl::strequal(var->name(), name)) return var->type() == type ? var : 0; // add new variable xpath_variable* result = impl::new_xpath_variable(type, name); if (result) { result->_next = _data[hash]; _data[hash] = result; } return result; } PUGI__FN bool xpath_variable_set::set(const char_t* name, bool value) { xpath_variable* var = add(name, xpath_type_boolean); return var ? var->set(value) : false; } PUGI__FN bool xpath_variable_set::set(const char_t* name, double value) { xpath_variable* var = add(name, xpath_type_number); return var ? var->set(value) : false; } PUGI__FN bool xpath_variable_set::set(const char_t* name, const char_t* value) { xpath_variable* var = add(name, xpath_type_string); return var ? var->set(value) : false; } PUGI__FN bool xpath_variable_set::set(const char_t* name, const xpath_node_set& value) { xpath_variable* var = add(name, xpath_type_node_set); return var ? var->set(value) : false; } PUGI__FN xpath_variable* xpath_variable_set::get(const char_t* name) { return _find(name); } PUGI__FN const xpath_variable* xpath_variable_set::get(const char_t* name) const { return _find(name); } PUGI__FN xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables): _impl(0) { impl::xpath_query_impl* qimpl = impl::xpath_query_impl::create(); if (!qimpl) { #ifdef PUGIXML_NO_EXCEPTIONS _result.error = "Out of memory"; #else throw std::bad_alloc(); #endif } else { using impl::auto_deleter; // MSVC7 workaround auto_deleter impl(qimpl, impl::xpath_query_impl::destroy); qimpl->root = impl::xpath_parser::parse(query, variables, &qimpl->alloc, &_result); if (qimpl->root) { qimpl->root->optimize(&qimpl->alloc); _impl = impl.release(); _result.error = 0; } } } PUGI__FN xpath_query::xpath_query(): _impl(0) { } PUGI__FN xpath_query::~xpath_query() { if (_impl) impl::xpath_query_impl::destroy(static_cast(_impl)); } #if __cplusplus >= 201103 PUGI__FN xpath_query::xpath_query(xpath_query&& rhs) { _impl = rhs._impl; rhs._impl = 0; } PUGI__FN xpath_query& xpath_query::operator=(xpath_query&& rhs) { if (this == &rhs) return *this; if (_impl) impl::xpath_query_impl::destroy(static_cast(_impl)); _impl = rhs._impl; rhs._impl = 0; return *this; } #endif PUGI__FN xpath_value_type xpath_query::return_type() const { if (!_impl) return xpath_type_none; return static_cast(_impl)->root->rettype(); } PUGI__FN bool xpath_query::evaluate_boolean(const xpath_node& n) const { if (!_impl) return false; impl::xpath_context c(n, 1, 1); impl::xpath_stack_data sd; #ifdef PUGIXML_NO_EXCEPTIONS if (setjmp(sd.error_handler)) return false; #endif return static_cast(_impl)->root->eval_boolean(c, sd.stack); } PUGI__FN double xpath_query::evaluate_number(const xpath_node& n) const { if (!_impl) return impl::gen_nan(); impl::xpath_context c(n, 1, 1); impl::xpath_stack_data sd; #ifdef PUGIXML_NO_EXCEPTIONS if (setjmp(sd.error_handler)) return impl::gen_nan(); #endif return static_cast(_impl)->root->eval_number(c, sd.stack); } #ifndef PUGIXML_NO_STL PUGI__FN string_t xpath_query::evaluate_string(const xpath_node& n) const { impl::xpath_stack_data sd; impl::xpath_string r = impl::evaluate_string_impl(static_cast(_impl), n, sd); return string_t(r.c_str(), r.length()); } #endif PUGI__FN size_t xpath_query::evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const { impl::xpath_stack_data sd; impl::xpath_string r = impl::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; } PUGI__FN xpath_node_set xpath_query::evaluate_node_set(const xpath_node& n) const { impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast(_impl)); if (!root) return xpath_node_set(); impl::xpath_context c(n, 1, 1); impl::xpath_stack_data sd; #ifdef PUGIXML_NO_EXCEPTIONS if (setjmp(sd.error_handler)) return xpath_node_set(); #endif impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_all); return xpath_node_set(r.begin(), r.end(), r.type()); } PUGI__FN xpath_node xpath_query::evaluate_node(const xpath_node& n) const { impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast(_impl)); if (!root) return xpath_node(); impl::xpath_context c(n, 1, 1); impl::xpath_stack_data sd; #ifdef PUGIXML_NO_EXCEPTIONS if (setjmp(sd.error_handler)) return xpath_node(); #endif impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_first); return r.first(); } PUGI__FN const xpath_parse_result& xpath_query::result() const { return _result; } PUGI__FN static void unspecified_bool_xpath_query(xpath_query***) { } PUGI__FN xpath_query::operator xpath_query::unspecified_bool_type() const { return _impl ? unspecified_bool_xpath_query : 0; } PUGI__FN bool xpath_query::operator!() const { return !_impl; } PUGI__FN xpath_node xml_node::select_node(const char_t* query, xpath_variable_set* variables) const { xpath_query q(query, variables); return select_node(q); } PUGI__FN xpath_node xml_node::select_node(const xpath_query& query) const { return query.evaluate_node(*this); } PUGI__FN xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables) const { xpath_query q(query, variables); return select_nodes(q); } PUGI__FN xpath_node_set xml_node::select_nodes(const xpath_query& query) const { return query.evaluate_node_set(*this); } PUGI__FN xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables) const { xpath_query q(query, variables); return select_single_node(q); } PUGI__FN xpath_node xml_node::select_single_node(const xpath_query& query) const { return query.evaluate_node(*this); } } #endif #ifdef __BORLANDC__ # pragma option pop #endif // Intel C++ does not properly keep warning state for function templates, // so popping warning state at the end of translation unit leads to warnings in the middle. #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) # pragma warning(pop) #endif // Undefine all local macros (makes sure we're not leaking macros in header-only mode) #undef PUGI__NO_INLINE #undef PUGI__UNLIKELY #undef PUGI__STATIC_ASSERT #undef PUGI__DMC_VOLATILE #undef PUGI__MSVC_CRT_VERSION #undef PUGI__NS_BEGIN #undef PUGI__NS_END #undef PUGI__FN #undef PUGI__FN_NO_INLINE #undef PUGI__GETPAGE_IMPL #undef PUGI__GETPAGE #undef PUGI__NODETYPE #undef PUGI__IS_CHARTYPE_IMPL #undef PUGI__IS_CHARTYPE #undef PUGI__IS_CHARTYPEX #undef PUGI__ENDSWITH #undef PUGI__SKIPWS #undef PUGI__OPTSET #undef PUGI__PUSHNODE #undef PUGI__POPNODE #undef PUGI__SCANFOR #undef PUGI__SCANWHILE #undef PUGI__SCANWHILE_UNROLL #undef PUGI__ENDSEG #undef PUGI__THROW_ERROR #undef PUGI__CHECK_ERROR #endif /** * Copyright (c) 2006-2015 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. */ ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/detail/pugixml.hpp ================================================ /** * pugixml parser - version 1.7 * -------------------------------------------------------- * Copyright (C) 2006-2015, 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 PUGIXML_VERSION // Define version macro; evaluates to major * 100 + minor so that it's safe to use in less-than comparisons # define PUGIXML_VERSION 170 #endif // Include user configuration file (this can define various configuration macros) #include "pugiconfig.hpp" #ifndef HEADER_PUGIXML_HPP #define HEADER_PUGIXML_HPP // Include stddef.h for size_t and ptrdiff_t #include // Include exception header for XPath #if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS) # include #endif // Include STL headers #ifndef PUGIXML_NO_STL # include # include # include #endif // Macro for deprecated features #ifndef PUGIXML_DEPRECATED # if defined(__GNUC__) # define PUGIXML_DEPRECATED __attribute__((deprecated)) # elif defined(_MSC_VER) && _MSC_VER >= 1300 # define PUGIXML_DEPRECATED __declspec(deprecated) # else # define PUGIXML_DEPRECATED # endif #endif // If no API is defined, assume default #ifndef PUGIXML_API # define PUGIXML_API #endif // If no API for classes is defined, assume default #ifndef PUGIXML_CLASS # define PUGIXML_CLASS PUGIXML_API #endif // If no API for functions is defined, assume default #ifndef PUGIXML_FUNCTION # define PUGIXML_FUNCTION PUGIXML_API #endif // If the platform is known to have long long support, enable long long functions #ifndef PUGIXML_HAS_LONG_LONG # if __cplusplus >= 201103 # define PUGIXML_HAS_LONG_LONG # elif defined(_MSC_VER) && _MSC_VER >= 1400 # define PUGIXML_HAS_LONG_LONG # endif #endif // Character interface macros #ifdef PUGIXML_WCHAR_MODE # define PUGIXML_TEXT(t) L ## t # define PUGIXML_CHAR wchar_t #else # define PUGIXML_TEXT(t) t # define PUGIXML_CHAR char #endif namespace pugi { // Character type used for all internal storage and operations; depends on PUGIXML_WCHAR_MODE typedef PUGIXML_CHAR char_t; #ifndef PUGIXML_NO_STL // String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE typedef std::basic_string, std::allocator > string_t; #endif } // The PugiXML namespace namespace pugi { // Tree node types enum xml_node_type { node_null, // Empty (null) node handle node_document, // A document tree's absolute root node_element, // Element tag, i.e. '' node_pcdata, // Plain character data, i.e. 'text' node_cdata, // Character data, i.e. '' node_comment, // Comment tag, i.e. '' node_pi, // Processing instruction, i.e. '' node_declaration, // Document declaration, i.e. '' node_doctype // Document type declaration, i.e. '' }; // Parsing options // Minimal parsing mode (equivalent to turning all other flags off). // Only elements and PCDATA sections are added to the DOM tree, no text conversions are performed. const unsigned int parse_minimal = 0x0000; // This flag determines if processing instructions (node_pi) are added to the DOM tree. This flag is off by default. const unsigned int parse_pi = 0x0001; // This flag determines if comments (node_comment) are added to the DOM tree. This flag is off by default. const unsigned int parse_comments = 0x0002; // This flag determines if CDATA sections (node_cdata) are added to the DOM tree. This flag is on by default. const unsigned int parse_cdata = 0x0004; // This flag determines if plain character data (node_pcdata) that consist only of whitespace are added to the DOM tree. // This flag is off by default; turning it on usually results in slower parsing and more memory consumption. const unsigned int parse_ws_pcdata = 0x0008; // This flag determines if character and entity references are expanded during parsing. This flag is on by default. const unsigned int parse_escapes = 0x0010; // This flag determines if EOL characters are normalized (converted to #xA) during parsing. This flag is on by default. const unsigned int parse_eol = 0x0020; // This flag determines if attribute values are normalized using CDATA normalization rules during parsing. This flag is on by default. const unsigned int parse_wconv_attribute = 0x0040; // This flag determines if attribute values are normalized using NMTOKENS normalization rules during parsing. This flag is off by default. const unsigned int parse_wnorm_attribute = 0x0080; // This flag determines if document declaration (node_declaration) is added to the DOM tree. This flag is off by default. const unsigned int parse_declaration = 0x0100; // This flag determines if document type declaration (node_doctype) is added to the DOM tree. This flag is off by default. const unsigned int parse_doctype = 0x0200; // This flag determines if plain character data (node_pcdata) that is the only child of the parent node and that consists only // of whitespace is added to the DOM tree. // This flag is off by default; turning it on may result in slower parsing and more memory consumption. const unsigned int parse_ws_pcdata_single = 0x0400; // This flag determines if leading and trailing whitespace is to be removed from plain character data. This flag is off by default. const unsigned int parse_trim_pcdata = 0x0800; // This flag determines if plain character data that does not have a parent node is added to the DOM tree, and if an empty document // is a valid document. This flag is off by default. const unsigned int parse_fragment = 0x1000; // The default parsing mode. // Elements, PCDATA and CDATA sections are added to the DOM tree, character/reference entities are expanded, // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. const unsigned int parse_default = parse_cdata | parse_escapes | parse_wconv_attribute | parse_eol; // The full parsing mode. // Nodes of all types are added to the DOM tree, character/reference entities are expanded, // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. const unsigned int parse_full = parse_default | parse_pi | parse_comments | parse_declaration | parse_doctype; // These flags determine the encoding of input data for XML document enum xml_encoding { encoding_auto, // Auto-detect input encoding using BOM or < / class xml_object_range { public: typedef It const_iterator; typedef It iterator; xml_object_range(It b, It e): _begin(b), _end(e) { } It begin() const { return _begin; } It end() const { return _end; } private: It _begin, _end; }; // Writer interface for node printing (see xml_node::print) class PUGIXML_CLASS xml_writer { public: virtual ~xml_writer() {} // Write memory chunk into stream/file/whatever virtual void write(const void* data, size_t size) = 0; }; // xml_writer implementation for FILE* class PUGIXML_CLASS xml_writer_file: public xml_writer { public: // Construct writer from a FILE* object; void* is used to avoid header dependencies on stdio xml_writer_file(void* file); virtual void write(const void* data, size_t size); private: void* file; }; #ifndef PUGIXML_NO_STL // xml_writer implementation for streams class PUGIXML_CLASS xml_writer_stream: public xml_writer { public: // Construct writer from an output stream object xml_writer_stream(std::basic_ostream >& stream); xml_writer_stream(std::basic_ostream >& stream); virtual void write(const void* data, size_t size); private: std::basic_ostream >* narrow_stream; std::basic_ostream >* wide_stream; }; #endif // A light-weight handle for manipulating attributes in DOM tree class PUGIXML_CLASS xml_attribute { friend class xml_attribute_iterator; friend class xml_node; private: xml_attribute_struct* _attr; typedef void (*unspecified_bool_type)(xml_attribute***); public: // Default constructor. Constructs an empty attribute. xml_attribute(); // Constructs attribute from internal pointer explicit xml_attribute(xml_attribute_struct* attr); // Safe bool conversion operator operator unspecified_bool_type() const; // Borland C++ workaround bool operator!() const; // Comparison operators (compares wrapped attribute pointers) bool operator==(const xml_attribute& r) const; bool operator!=(const xml_attribute& r) const; bool operator<(const xml_attribute& r) const; bool operator>(const xml_attribute& r) const; bool operator<=(const xml_attribute& r) const; bool operator>=(const xml_attribute& r) const; // Check if attribute is empty bool empty() const; // Get attribute name/value, or "" if attribute is empty const char_t* name() const; const char_t* value() const; // Get attribute value, or the default value if attribute is empty const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; // Get attribute value as a number, or the default value if conversion did not succeed or attribute is empty int as_int(int def = 0) const; unsigned int as_uint(unsigned int def = 0) const; double as_double(double def = 0) const; float as_float(float def = 0) const; #ifdef PUGIXML_HAS_LONG_LONG long long as_llong(long long def = 0) const; unsigned long long as_ullong(unsigned long long def = 0) const; #endif // Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if attribute is empty bool as_bool(bool def = false) const; // Set attribute name/value (returns false if attribute is empty or there is not enough memory) bool set_name(const char_t* rhs); bool set_value(const char_t* rhs); // Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") bool set_value(int rhs); bool set_value(unsigned int rhs); bool set_value(double rhs); bool set_value(float rhs); bool set_value(bool rhs); #ifdef PUGIXML_HAS_LONG_LONG bool set_value(long long rhs); bool set_value(unsigned long long rhs); #endif // Set attribute value (equivalent to set_value without error checking) xml_attribute& operator=(const char_t* rhs); xml_attribute& operator=(int rhs); xml_attribute& operator=(unsigned int rhs); xml_attribute& operator=(double rhs); xml_attribute& operator=(float rhs); xml_attribute& operator=(bool rhs); #ifdef PUGIXML_HAS_LONG_LONG xml_attribute& operator=(long long rhs); xml_attribute& operator=(unsigned long long rhs); #endif // Get next/previous attribute in the attribute list of the parent node xml_attribute next_attribute() const; xml_attribute previous_attribute() const; // Get hash value (unique for handles to the same object) size_t hash_value() const; // Get internal pointer xml_attribute_struct* internal_object() const; }; #ifdef __BORLANDC__ // Borland C++ workaround bool PUGIXML_FUNCTION operator&&(const xml_attribute& lhs, bool rhs); bool PUGIXML_FUNCTION operator||(const xml_attribute& lhs, bool rhs); #endif // A light-weight handle for manipulating nodes in DOM tree class PUGIXML_CLASS xml_node { friend class xml_attribute_iterator; friend class xml_node_iterator; friend class xml_named_node_iterator; protected: xml_node_struct* _root; typedef void (*unspecified_bool_type)(xml_node***); public: // Default constructor. Constructs an empty node. xml_node(); // Constructs node from internal pointer explicit xml_node(xml_node_struct* p); // Safe bool conversion operator operator unspecified_bool_type() const; // Borland C++ workaround bool operator!() const; // Comparison operators (compares wrapped node pointers) bool operator==(const xml_node& r) const; bool operator!=(const xml_node& r) const; bool operator<(const xml_node& r) const; bool operator>(const xml_node& r) const; bool operator<=(const xml_node& r) const; bool operator>=(const xml_node& r) const; // Check if node is empty. bool empty() const; // Get node type xml_node_type type() const; // Get node name, or "" if node is empty or it has no name const char_t* name() const; // Get node value, or "" if node is empty or it has no value // Note: For text node.value() does not return "text"! Use child_value() or text() methods to access text inside nodes. const char_t* value() const; // Get attribute list xml_attribute first_attribute() const; xml_attribute last_attribute() const; // Get children list xml_node first_child() const; xml_node last_child() const; // Get next/previous sibling in the children list of the parent node xml_node next_sibling() const; xml_node previous_sibling() const; // Get parent node xml_node parent() const; // Get root of DOM tree this node belongs to xml_node root() const; // Get text object for the current node xml_text text() const; // Get child, attribute or next/previous sibling with the specified name xml_node child(const char_t* name) const; xml_attribute attribute(const char_t* name) const; xml_node next_sibling(const char_t* name) const; xml_node previous_sibling(const char_t* name) const; // Get attribute, starting the search from a hint (and updating hint so that searching for a sequence of attributes is fast) xml_attribute attribute(const char_t* name, xml_attribute& hint) const; // Get child value of current node; that is, value of the first child node of type PCDATA/CDATA const char_t* child_value() const; // Get child value of child with specified name. Equivalent to child(name).child_value(). const char_t* child_value(const char_t* name) const; // Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value) bool set_name(const char_t* rhs); bool set_value(const char_t* rhs); // Add attribute with specified name. Returns added attribute, or empty attribute on errors. xml_attribute append_attribute(const char_t* name); xml_attribute prepend_attribute(const char_t* name); xml_attribute insert_attribute_after(const char_t* name, const xml_attribute& attr); xml_attribute insert_attribute_before(const char_t* name, const xml_attribute& attr); // Add a copy of the specified attribute. Returns added attribute, or empty attribute on errors. xml_attribute append_copy(const xml_attribute& proto); xml_attribute prepend_copy(const xml_attribute& proto); xml_attribute insert_copy_after(const xml_attribute& proto, const xml_attribute& attr); xml_attribute insert_copy_before(const xml_attribute& proto, const xml_attribute& attr); // Add child node with specified type. Returns added node, or empty node on errors. xml_node append_child(xml_node_type type = node_element); xml_node prepend_child(xml_node_type type = node_element); xml_node insert_child_after(xml_node_type type, const xml_node& node); xml_node insert_child_before(xml_node_type type, const xml_node& node); // Add child element with specified name. Returns added node, or empty node on errors. xml_node append_child(const char_t* name); xml_node prepend_child(const char_t* name); xml_node insert_child_after(const char_t* name, const xml_node& node); xml_node insert_child_before(const char_t* name, const xml_node& node); // Add a copy of the specified node as a child. Returns added node, or empty node on errors. xml_node append_copy(const xml_node& proto); xml_node prepend_copy(const xml_node& proto); xml_node insert_copy_after(const xml_node& proto, const xml_node& node); xml_node insert_copy_before(const xml_node& proto, const xml_node& node); // Move the specified node to become a child of this node. Returns moved node, or empty node on errors. xml_node append_move(const xml_node& moved); xml_node prepend_move(const xml_node& moved); xml_node insert_move_after(const xml_node& moved, const xml_node& node); xml_node insert_move_before(const xml_node& moved, const xml_node& node); // Remove specified attribute bool remove_attribute(const xml_attribute& a); bool remove_attribute(const char_t* name); // Remove specified child bool remove_child(const xml_node& n); bool remove_child(const char_t* name); // Parses buffer as an XML document fragment and appends all nodes as children of the current node. // Copies/converts the buffer, so it may be deleted or changed after the function returns. // Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes does not immediately reclaim that memory. xml_parse_result append_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); // Find attribute using predicate. Returns first attribute for which predicate returned true. template xml_attribute find_attribute(Predicate pred) const { if (!_root) return xml_attribute(); for (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute()) if (pred(attrib)) return attrib; return xml_attribute(); } // Find child node using predicate. Returns first child for which predicate returned true. template xml_node find_child(Predicate pred) const { if (!_root) return xml_node(); for (xml_node node = first_child(); node; node = node.next_sibling()) if (pred(node)) return node; return xml_node(); } // Find node from subtree using predicate. Returns first node from subtree (depth-first), for which predicate returned true. template xml_node find_node(Predicate pred) const { if (!_root) return xml_node(); xml_node cur = first_child(); while (cur._root && cur._root != _root) { if (pred(cur)) return cur; if (cur.first_child()) cur = cur.first_child(); else if (cur.next_sibling()) cur = cur.next_sibling(); else { while (!cur.next_sibling() && cur._root != _root) cur = cur.parent(); if (cur._root != _root) cur = cur.next_sibling(); } } return xml_node(); } // Find child node by attribute name/value xml_node find_child_by_attribute(const char_t* name, const char_t* attr_name, const char_t* attr_value) const; xml_node find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const; #ifndef PUGIXML_NO_STL // Get the absolute node path from root as a text string. string_t path(char_t delimiter = '/') const; #endif // Search for a node by path consisting of node names and . or .. elements. xml_node first_element_by_path(const char_t* path, char_t delimiter = '/') const; // Recursively traverse subtree with xml_tree_walker bool traverse(xml_tree_walker& walker); #ifndef PUGIXML_NO_XPATH // Select single node by evaluating XPath query. Returns first node from the resulting node set. xpath_node select_node(const char_t* query, xpath_variable_set* variables = 0) const; xpath_node select_node(const xpath_query& query) const; // Select node set by evaluating XPath query xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const; xpath_node_set select_nodes(const xpath_query& query) const; // (deprecated: use select_node instead) Select single node by evaluating XPath query. 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; #endif // Print subtree using a writer object void print(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; #ifndef PUGIXML_NO_STL // Print subtree to stream void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, unsigned int depth = 0) const; #endif // Child nodes iterators typedef xml_node_iterator iterator; iterator begin() const; iterator end() const; // Attribute iterators typedef xml_attribute_iterator attribute_iterator; attribute_iterator attributes_begin() const; attribute_iterator attributes_end() const; // Range-based for support xml_object_range children() const; xml_object_range children(const char_t* name) const; xml_object_range attributes() const; // Get node offset in parsed file/string (in char_t units) for debugging purposes ptrdiff_t offset_debug() const; // Get hash value (unique for handles to the same object) size_t hash_value() const; // Get internal pointer xml_node_struct* internal_object() const; }; #ifdef __BORLANDC__ // Borland C++ workaround bool PUGIXML_FUNCTION operator&&(const xml_node& lhs, bool rhs); bool PUGIXML_FUNCTION operator||(const xml_node& lhs, bool rhs); #endif // A helper for working with text inside PCDATA nodes class PUGIXML_CLASS xml_text { friend class xml_node; xml_node_struct* _root; typedef void (*unspecified_bool_type)(xml_text***); explicit xml_text(xml_node_struct* root); xml_node_struct* _data_new(); xml_node_struct* _data() const; public: // Default constructor. Constructs an empty object. xml_text(); // Safe bool conversion operator operator unspecified_bool_type() const; // Borland C++ workaround bool operator!() const; // Check if text object is empty bool empty() const; // Get text, or "" if object is empty const char_t* get() const; // Get text, or the default value if object is empty const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; // Get text as a number, or the default value if conversion did not succeed or object is empty int as_int(int def = 0) const; unsigned int as_uint(unsigned int def = 0) const; double as_double(double def = 0) const; float as_float(float def = 0) const; #ifdef PUGIXML_HAS_LONG_LONG long long as_llong(long long def = 0) const; unsigned long long as_ullong(unsigned long long def = 0) const; #endif // Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty bool as_bool(bool def = false) const; // Set text (returns false if object is empty or there is not enough memory) bool set(const char_t* rhs); // Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") bool set(int rhs); bool set(unsigned int rhs); bool set(double rhs); bool set(float rhs); bool set(bool rhs); #ifdef PUGIXML_HAS_LONG_LONG bool set(long long rhs); bool set(unsigned long long rhs); #endif // Set text (equivalent to set without error checking) xml_text& operator=(const char_t* rhs); xml_text& operator=(int rhs); xml_text& operator=(unsigned int rhs); xml_text& operator=(double rhs); xml_text& operator=(float rhs); xml_text& operator=(bool rhs); #ifdef PUGIXML_HAS_LONG_LONG xml_text& operator=(long long rhs); xml_text& operator=(unsigned long long rhs); #endif // Get the data node (node_pcdata or node_cdata) for this object xml_node data() const; }; #ifdef __BORLANDC__ // Borland C++ workaround bool PUGIXML_FUNCTION operator&&(const xml_text& lhs, bool rhs); bool PUGIXML_FUNCTION operator||(const xml_text& lhs, bool rhs); #endif // Child node iterator (a bidirectional iterator over a collection of xml_node) class PUGIXML_CLASS xml_node_iterator { friend class xml_node; private: mutable xml_node _wrap; xml_node _parent; xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent); public: // Iterator traits typedef ptrdiff_t difference_type; typedef xml_node value_type; typedef xml_node* pointer; typedef xml_node& reference; #ifndef PUGIXML_NO_STL typedef std::bidirectional_iterator_tag iterator_category; #endif // Default constructor xml_node_iterator(); // Construct an iterator which points to the specified node xml_node_iterator(const xml_node& node); // Iterator operators bool operator==(const xml_node_iterator& rhs) const; bool operator!=(const xml_node_iterator& rhs) const; xml_node& operator*() const; xml_node* operator->() const; const xml_node_iterator& operator++(); xml_node_iterator operator++(int); const xml_node_iterator& operator--(); xml_node_iterator operator--(int); }; // Attribute iterator (a bidirectional iterator over a collection of xml_attribute) class PUGIXML_CLASS xml_attribute_iterator { friend class xml_node; private: mutable xml_attribute _wrap; xml_node _parent; xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent); public: // Iterator traits typedef ptrdiff_t difference_type; typedef xml_attribute value_type; typedef xml_attribute* pointer; typedef xml_attribute& reference; #ifndef PUGIXML_NO_STL typedef std::bidirectional_iterator_tag iterator_category; #endif // Default constructor xml_attribute_iterator(); // Construct an iterator which points to the specified attribute xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent); // Iterator operators bool operator==(const xml_attribute_iterator& rhs) const; bool operator!=(const xml_attribute_iterator& rhs) const; xml_attribute& operator*() const; xml_attribute* operator->() const; const xml_attribute_iterator& operator++(); xml_attribute_iterator operator++(int); const xml_attribute_iterator& operator--(); xml_attribute_iterator operator--(int); }; // Named node range helper class PUGIXML_CLASS xml_named_node_iterator { friend class xml_node; public: // Iterator traits typedef ptrdiff_t difference_type; typedef xml_node value_type; typedef xml_node* pointer; typedef xml_node& reference; #ifndef PUGIXML_NO_STL typedef std::bidirectional_iterator_tag iterator_category; #endif // Default constructor xml_named_node_iterator(); // Construct an iterator which points to the specified node xml_named_node_iterator(const xml_node& node, const char_t* name); // Iterator operators bool operator==(const xml_named_node_iterator& rhs) const; bool operator!=(const xml_named_node_iterator& rhs) const; xml_node& operator*() const; xml_node* operator->() const; const xml_named_node_iterator& operator++(); xml_named_node_iterator operator++(int); const xml_named_node_iterator& operator--(); xml_named_node_iterator operator--(int); private: mutable xml_node _wrap; xml_node _parent; const char_t* _name; xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name); }; // Abstract tree walker class (see xml_node::traverse) class PUGIXML_CLASS xml_tree_walker { friend class xml_node; private: int _depth; protected: // Get current traversal depth int depth() const; public: xml_tree_walker(); virtual ~xml_tree_walker(); // Callback that is called when traversal begins virtual bool begin(xml_node& node); // Callback that is called for each node traversed virtual bool for_each(xml_node& node) = 0; // Callback that is called when traversal ends virtual bool end(xml_node& node); }; // Parsing status, returned as part of xml_parse_result object enum xml_parse_status { status_ok = 0, // No error status_file_not_found, // File was not found during load_file() status_io_error, // Error reading from file/stream status_out_of_memory, // Could not allocate memory status_internal_error, // Internal error occurred status_unrecognized_tag, // Parser could not determine tag type status_bad_pi, // Parsing error occurred while parsing document declaration/processing instruction status_bad_comment, // Parsing error occurred while parsing comment status_bad_cdata, // Parsing error occurred while parsing CDATA section status_bad_doctype, // Parsing error occurred while parsing document type declaration status_bad_pcdata, // Parsing error occurred while parsing PCDATA section status_bad_start_element, // Parsing error occurred while parsing start element tag status_bad_attribute, // Parsing error occurred while parsing element attribute status_bad_end_element, // Parsing error occurred while parsing end element tag status_end_element_mismatch,// There was a mismatch of start-end tags (closing tag had incorrect name, some tag was not closed or there was an excessive closing tag) status_append_invalid_root, // Unable to append nodes since root type is not node_element or node_document (exclusive to xml_node::append_buffer) status_no_document_element // Parsing resulted in a document without element nodes }; // Parsing result struct PUGIXML_CLASS xml_parse_result { // Parsing status (see xml_parse_status) xml_parse_status status; // Last parsed offset (in char_t units from start of input data) ptrdiff_t offset; // Source document encoding xml_encoding encoding; // Default constructor, initializes object to failed state xml_parse_result(); // Cast to bool operator operator bool() const; // Get error description const char* description() const; }; // Document class (DOM tree root) class PUGIXML_CLASS xml_document: public xml_node { private: char_t* _buffer; char _memory[192]; // Non-copyable semantics xml_document(const xml_document&); xml_document& operator=(const xml_document&); void create(); void destroy(); 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 // (deprecated: use load_string instead) 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 zero-terminated string. No encoding conversions are applied. xml_parse_result load_string(const char_t* contents, unsigned int options = parse_default); // Load document from file xml_parse_result load_file(const char* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); xml_parse_result load_file(const wchar_t* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); // Load document from buffer. Copies/converts the buffer, so it may be deleted or changed after the function returns. xml_parse_result load_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). // You should ensure that buffer data will persist throughout the document's lifetime, and free the buffer memory manually once document is destroyed. xml_parse_result load_buffer_inplace(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). // You should allocate the buffer with pugixml allocation function; document will free the buffer when it is no longer needed (you can't use it anymore). xml_parse_result load_buffer_inplace_own(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); // Save XML document to writer (semantics is slightly different from xml_node::print, see documentation for details). void save(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; #ifndef PUGIXML_NO_STL // Save XML document to stream (semantics is slightly different from xml_node::print, see documentation for details). void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default) const; #endif // Save XML to file bool save_file(const char* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; bool save_file(const wchar_t* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; // Get document element xml_node document_element() const; }; #ifndef PUGIXML_NO_XPATH // XPath query return type enum xpath_value_type { xpath_type_none, // Unknown type (query failed to compile) xpath_type_node_set, // Node set (xpath_node_set) xpath_type_number, // Number xpath_type_string, // String xpath_type_boolean // Boolean }; // XPath parsing result struct PUGIXML_CLASS xpath_parse_result { // Error message (0 if no error) const char* error; // Last parsed offset (in char_t units from string start) ptrdiff_t offset; // Default constructor, initializes object to failed state xpath_parse_result(); // Cast to bool operator operator bool() const; // Get error description const char* description() const; }; // A single XPath variable class PUGIXML_CLASS xpath_variable { friend class xpath_variable_set; protected: xpath_value_type _type; xpath_variable* _next; xpath_variable(xpath_value_type type); // Non-copyable semantics xpath_variable(const xpath_variable&); xpath_variable& operator=(const xpath_variable&); public: // Get variable name const char_t* name() const; // Get variable type xpath_value_type type() const; // Get variable value; no type conversion is performed, default value (false, NaN, empty string, empty node set) is returned on type mismatch error bool get_boolean() const; double get_number() const; const char_t* get_string() const; const xpath_node_set& get_node_set() const; // Set variable value; no type conversion is performed, false is returned on type mismatch error bool set(bool value); bool set(double value); bool set(const char_t* value); bool set(const xpath_node_set& value); }; // A set of XPath variables class PUGIXML_CLASS xpath_variable_set { private: xpath_variable* _data[64]; void _assign(const xpath_variable_set& rhs); void _swap(xpath_variable_set& rhs); xpath_variable* _find(const char_t* name) const; static bool _clone(xpath_variable* var, xpath_variable** out_result); static void _destroy(xpath_variable* var); public: // Default constructor/destructor xpath_variable_set(); ~xpath_variable_set(); // Copy constructor/assignment operator xpath_variable_set(const xpath_variable_set& rhs); xpath_variable_set& operator=(const xpath_variable_set& rhs); #if __cplusplus >= 201103 // Move semantics support xpath_variable_set(xpath_variable_set&& rhs); xpath_variable_set& operator=(xpath_variable_set&& rhs); #endif // Add a new variable or get the existing one, if the types match xpath_variable* add(const char_t* name, xpath_value_type type); // Set value of an existing variable; no type conversion is performed, false is returned if there is no such variable or if types mismatch bool set(const char_t* name, bool value); bool set(const char_t* name, double value); bool set(const char_t* name, const char_t* value); bool set(const char_t* name, const xpath_node_set& value); // Get existing variable by name xpath_variable* get(const char_t* name); const xpath_variable* get(const char_t* name) const; }; // A compiled XPath query object class PUGIXML_CLASS xpath_query { private: void* _impl; xpath_parse_result _result; typedef void (*unspecified_bool_type)(xpath_query***); // Non-copyable semantics xpath_query(const xpath_query&); xpath_query& operator=(const xpath_query&); public: // Construct a compiled object from XPath expression. // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors. explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0); // Constructor xpath_query(); // Destructor ~xpath_query(); #if __cplusplus >= 201103 // Move semantics support xpath_query(xpath_query&& rhs); xpath_query& operator=(xpath_query&& rhs); #endif // Get query expression return type xpath_value_type return_type() const; // Evaluate expression as boolean value in the specified context; performs type conversion if necessary. // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. bool evaluate_boolean(const xpath_node& n) const; // Evaluate expression as double value in the specified context; performs type conversion if necessary. // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. double evaluate_number(const xpath_node& n) const; #ifndef PUGIXML_NO_STL // Evaluate expression as string value in the specified context; performs type conversion if necessary. // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. string_t evaluate_string(const xpath_node& n) const; #endif // Evaluate expression as string value in the specified context; performs type conversion if necessary. // At most capacity characters are written to the destination buffer, full result size is returned (includes terminating zero). // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. // If PUGIXML_NO_EXCEPTIONS is defined, returns empty set instead. size_t evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const; // Evaluate expression as node set in the specified context. // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors. // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead. xpath_node_set evaluate_node_set(const xpath_node& n) const; // Evaluate expression as node set in the specified context. // Return first node in document order, or empty node if node set is empty. // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors. // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node instead. xpath_node evaluate_node(const xpath_node& n) const; // Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode) const xpath_parse_result& result() const; // Safe bool conversion operator operator unspecified_bool_type() const; // Borland C++ workaround bool operator!() const; }; #ifndef PUGIXML_NO_EXCEPTIONS // 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 void (*unspecified_bool_type)(xpath_node***); public: // Default constructor; constructs empty XPath node xpath_node(); // Construct XPath node from XML node/attribute xpath_node(const xml_node& node); xpath_node(const xml_attribute& attribute, const xml_node& parent); // Get node/attribute, if any xml_node node() const; xml_attribute attribute() const; // Get parent of contained node/attribute xml_node parent() const; // Safe bool conversion operator operator unspecified_bool_type() const; // Borland C++ workaround bool operator!() const; // Comparison operators bool operator==(const xpath_node& n) const; bool operator!=(const xpath_node& n) const; }; #ifdef __BORLANDC__ // Borland C++ workaround bool PUGIXML_FUNCTION operator&&(const xpath_node& lhs, bool rhs); bool PUGIXML_FUNCTION operator||(const xpath_node& lhs, bool rhs); #endif // A fixed-size collection of XPath nodes class PUGIXML_CLASS xpath_node_set { public: // Collection type enum type_t { type_unsorted, // Not ordered type_sorted, // Sorted by document order (ascending) type_sorted_reverse // Sorted by document order (descending) }; // Constant iterator type typedef const xpath_node* const_iterator; // We define non-constant iterator to be the same as constant iterator so that various generic algorithms (i.e. boost foreach) work typedef const xpath_node* iterator; // Default constructor. Constructs empty set. xpath_node_set(); // Constructs a set from iterator range; data is not checked for duplicates and is not sorted according to provided type, so be careful xpath_node_set(const_iterator begin, const_iterator end, type_t type = type_unsorted); // Destructor ~xpath_node_set(); // Copy constructor/assignment operator xpath_node_set(const xpath_node_set& ns); xpath_node_set& operator=(const xpath_node_set& ns); #if __cplusplus >= 201103 // Move semantics support xpath_node_set(xpath_node_set&& rhs); xpath_node_set& operator=(xpath_node_set&& rhs); #endif // Get collection type type_t type() const; // Get collection size size_t size() const; // Indexing operator const xpath_node& operator[](size_t index) const; // Collection iterators const_iterator begin() const; const_iterator end() const; // Sort the collection in ascending/descending order by document order void sort(bool reverse = false); // Get first node in the collection by document order xpath_node first() const; // Check if collection is empty bool empty() const; private: type_t _type; xpath_node _storage; xpath_node* _begin; xpath_node* _end; void _assign(const_iterator begin, const_iterator end, type_t type); void _move(xpath_node_set& rhs); }; #endif #ifndef PUGIXML_NO_STL // Convert wide string to UTF8 std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const wchar_t* str); std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const std::basic_string, std::allocator >& str); // Convert UTF8 to wide string std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const char* str); std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const std::basic_string, std::allocator >& str); #endif // Memory allocation function interface; returns pointer to allocated memory or NULL on failure typedef void* (*allocation_function)(size_t size); // Memory deallocation function interface typedef void (*deallocation_function)(void* ptr); // Override default memory management functions. All subsequent allocations/deallocations will be performed via supplied functions. void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate); // Get current memory management functions allocation_function PUGIXML_FUNCTION get_memory_allocation_function(); deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function(); } #if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC)) namespace std { // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_node_iterator&); std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_attribute_iterator&); std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator&); } #endif #if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) namespace std { // Workarounds for (non-standard) iterator category detection std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_node_iterator&); std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_attribute_iterator&); std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator&); } #endif #endif // Make sure implementation is included in header-only mode // Use macro expansion in #include to work around QMake (QTBUG-11923) #if defined(PUGIXML_HEADER_ONLY) && !defined(PUGIXML_SOURCE) # define PUGIXML_SOURCE "pugixml.cpp" # include PUGIXML_SOURCE #endif /** * Copyright (c) 2006-2015 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. */ ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/miniz.c ================================================ /* miniz.c v1.15 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing See "unlicense" statement at the end of this file. Rich Geldreich , last updated Oct. 13, 2013 Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). * Change History 10/13/13 v1.15 r4 - Interim bugfix release while I work on the next major release with Zip64 support (almost there!): - Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com) which could cause locate files to not find files. This bug would only have occured in earlier versions if you explicitly used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or mz_zip_add_mem_to_archive_file_in_place() (which used this flag). If you can't switch to v1.15 but want to fix this bug, just remove the uses of this flag from both helper funcs (and of course don't use the flag). - Bugfix in mz_zip_reader_extract_to_mem_no_alloc() from kymoon when pUser_read_buf is not NULL and compressed size is > uncompressed size - Fixing mz_zip_reader_extract_*() funcs so they don't try to extract compressed data from directory entries, to account for weird zipfiles which contain zero-size compressed data on dir entries. Hopefully this fix won't cause any issues on weird zip archives, because it assumes the low 16-bits of zip external attributes are DOS attributes (which I believe they always are in practice). - Fixing mz_zip_reader_is_file_a_directory() so it doesn't check the internal attributes, just the filename and external attributes - mz_zip_reader_init_file() - missing MZ_FCLOSE() call if the seek failed - Added cmake support for Linux builds which builds all the examples, tested with clang v3.3 and gcc v4.6. - Clang fix for tdefl_write_image_to_png_file_in_memory() from toffaletti - Merged MZ_FORCEINLINE fix from hdeanclark - Fix include before config #ifdef, thanks emil.brink - Added tdefl_write_image_to_png_file_in_memory_ex(): supports Y flipping (super useful for OpenGL apps), and explicit control over the compression level (so you can set it to 1 for real-time compression). - Merged in some compiler fixes from paulharris's github repro. - Retested this build under Windows (VS 2010, including static analysis), tcc 0.9.26, gcc v4.6 and clang v3.3. - Added example6.c, which dumps an image of the mandelbrot set to a PNG file. - Modified example2 to help test the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY flag more. - In r3: Bugfix to mz_zip_writer_add_file() found during merge: Fix possible src file fclose() leak if alignment bytes+local header file write faiiled - In r4: Minor bugfix to mz_zip_writer_add_from_zip_reader(): Was pushing the wrong central dir header offset, appears harmless in this release, but it became a problem in the zip64 branch 5/20/12 v1.14 - MinGW32/64 GCC 4.6.1 compiler fixes: added MZ_FORCEINLINE, #include (thanks fermtect). 5/19/12 v1.13 - From jason@cornsyrup.org and kelwert@mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit. - Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files. - Eliminated a bunch of warnings when compiling with GCC 32-bit/64. - Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly "Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning). - Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64. - Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test. - Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives. - Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.) - Fix ftell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself). 4/12/12 v1.12 - More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's. level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson for the feedback/bug report. 5/28/11 v1.11 - Added statement from unlicense.org 5/27/11 v1.10 - Substantial compressor optimizations: - Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a - Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86). - Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types. - Refactored the compression code for better readability and maintainability. - Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large drop in throughput on some files). 5/15/11 v1.09 - Initial stable release. * Low-level Deflate/Inflate implementation notes: Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses approximately as well as zlib. Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory block large enough to hold the entire file. The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. * zlib-style API notes: miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in zlib replacement in many apps: The z_stream struct, optional memory allocation callbacks deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound inflateInit/inflateInit2/inflate/inflateEnd compress, compress2, compressBound, uncompress CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. Supports raw deflate streams or standard zlib streams with adler-32 checking. Limitations: The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but there are no guarantees that miniz.c pulls this off perfectly. * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by Alex Evans. Supports 1-4 bytes/pixel images. * ZIP archive API notes: The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to get the job done with minimal fuss. There are simple API's to retrieve file information, read files from existing archives, create new archives, append new files to existing archives, or clone archive data from one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), or you can specify custom file read/write callbacks. - Archive reading: Just call this function to read a single file from a disk archive: void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags); For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); The locate operation can optionally check file comments too, which (as one example) can be used to identify multiple versions of the same file in an archive. This function uses a simple linear search through the central directory, so it's not very fast. Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and retrieve detailed info on each file by calling mz_zip_reader_file_stat(). - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data to disk and builds an exact image of the central directory in memory. The central directory image is written all at once at the end of the archive file when the archive is finalized. The archive writer can optionally align each file's local header and file data to any power of 2 alignment, which can be useful when the archive will be read from optical media. Also, the writer supports placing arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still readable by any ZIP tool. - Archive appending: The simple way to add a single file to an archive is to call this function: mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); The archive will be created if it doesn't already exist, otherwise it'll be appended to. Note the appending is done in-place and is not an atomic operation, so if something goes wrong during the operation it's possible the archive could be left without a central directory (although the local file headers and file data will be fine, so the archive will be recoverable). For more complex archive modification scenarios: 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and you're done. This is safe but requires a bunch of temporary disk space or heap memory. 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), append new files as needed, then finalize the archive which will write an updated central directory to the original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a possibility that the archive's central directory could be lost with this method if anything goes wrong, though. - ZIP archive support limitations: No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files. Requires streams capable of seeking. * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. * Important: For best perf. be sure to customize the below macros for your target platform: #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 #define MINIZ_LITTLE_ENDIAN 1 #define MINIZ_HAS_64BIT_REGISTERS 1 * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). */ #ifndef MINIZ_HEADER_INCLUDED #define MINIZ_HEADER_INCLUDED #include // Defines to completely disable specific portions of miniz.c: // If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. // Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. #define MINIZ_NO_STDIO // If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or // get/set file times, and the C run-time funcs that get/set times won't be called. // The current downside is the times written to your archives will be from 1979. #define MINIZ_NO_TIME // Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. #define MINIZ_NO_ARCHIVE_APIS // Define MINIZ_NO_ARCHIVE_APIS to disable all writing related ZIP archive API's. #define MINIZ_NO_ARCHIVE_WRITING_APIS // Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. //#define MINIZ_NO_ZLIB_APIS // Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. //#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES // Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. // Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc // callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user // functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. //#define MINIZ_NO_MALLOC #if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) // TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux #define MINIZ_NO_TIME #endif #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) #include #endif #if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) // MINIZ_X86_OR_X64_CPU is only used to help set the below macros. #define MINIZ_X86_OR_X64_CPU 1 #endif #if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU // Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. #define MINIZ_LITTLE_ENDIAN 1 #endif #if MINIZ_X86_OR_X64_CPU // Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 #endif #if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) // Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). #define MINIZ_HAS_64BIT_REGISTERS 1 #endif #ifdef __cplusplus extern "C" { #endif // ------------------- zlib-style API Definitions. // For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! typedef unsigned long mz_ulong; // mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. void mz_free(void *p); #define MZ_ADLER32_INIT (1) // mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); #define MZ_CRC32_INIT (0) // mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); // Compression strategies. enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 }; // Method #define MZ_DEFLATED 8 #ifndef MINIZ_NO_ZLIB_APIS // Heap allocation callbacks. // Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long. typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); typedef void (*mz_free_func)(void *opaque, void *address); typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); #define MZ_VERSION "9.1.15" #define MZ_VERNUM 0x91F0 #define MZ_VER_MAJOR 9 #define MZ_VER_MINOR 1 #define MZ_VER_REVISION 15 #define MZ_VER_SUBREVISION 0 // Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 }; // Return status codes. MZ_PARAM_ERROR is non-standard. enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 }; // Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_UBER_COMPRESSION = 10, MZ_DEFAULT_LEVEL = 6, MZ_DEFAULT_COMPRESSION = -1 }; // Window bits #define MZ_DEFAULT_WINDOW_BITS 15 struct mz_internal_state; // Compression/decompression stream struct. typedef struct mz_stream_s { const unsigned char *next_in; // pointer to next byte to read unsigned int avail_in; // number of bytes available at next_in mz_ulong total_in; // total number of bytes consumed so far unsigned char *next_out; // pointer to next byte to write unsigned int avail_out; // number of bytes that can be written to next_out mz_ulong total_out; // total number of bytes produced so far char *msg; // error msg (unused) struct mz_internal_state *state; // internal state, allocated by zalloc/zfree mz_alloc_func zalloc; // optional heap allocation function (defaults to malloc) mz_free_func zfree; // optional heap free function (defaults to free) void *opaque; // heap alloc function user pointer int data_type; // data_type (unused) mz_ulong adler; // adler32 of the source or uncompressed data mz_ulong reserved; // not used } mz_stream; typedef mz_stream *mz_streamp; // Returns the version string of miniz.c. const char *mz_version(void); // mz_deflateInit() initializes a compressor with default options: // Parameters: // pStream must point to an initialized mz_stream struct. // level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. // level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. // (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) // Return values: // MZ_OK on success. // MZ_STREAM_ERROR if the stream is bogus. // MZ_PARAM_ERROR if the input parameters are bogus. // MZ_MEM_ERROR on out of memory. int mz_deflateInit(mz_streamp pStream, int level); // mz_deflateInit2() is like mz_deflate(), except with more control: // Additional parameters: // method must be MZ_DEFLATED // window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) // mem_level must be between [1, 9] (it's checked but ignored by miniz.c) int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); // Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). int mz_deflateReset(mz_streamp pStream); // mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. // Parameters: // pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. // flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. // Return values: // MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). // MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. // MZ_STREAM_ERROR if the stream is bogus. // MZ_PARAM_ERROR if one of the parameters is invalid. // MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) int mz_deflate(mz_streamp pStream, int flush); // mz_deflateEnd() deinitializes a compressor: // Return values: // MZ_OK on success. // MZ_STREAM_ERROR if the stream is bogus. int mz_deflateEnd(mz_streamp pStream); // mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); // Single-call compression functions mz_compress() and mz_compress2(): // Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); // mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). mz_ulong mz_compressBound(mz_ulong source_len); // Initializes a decompressor. int mz_inflateInit(mz_streamp pStream); // mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: // window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). int mz_inflateInit2(mz_streamp pStream, int window_bits); // Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. // Parameters: // pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. // flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. // On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). // MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. // Return values: // MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. // MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. // MZ_STREAM_ERROR if the stream is bogus. // MZ_DATA_ERROR if the deflate stream is invalid. // MZ_PARAM_ERROR if one of the parameters is invalid. // MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again // with more input data, or with more room in the output buffer (except when using single call decompression, described above). int mz_inflate(mz_streamp pStream, int flush); // Deinitializes a decompressor. int mz_inflateEnd(mz_streamp pStream); // Single-call decompression. // Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); // Returns a string description of the specified error code, or NULL if the error code is invalid. const char *mz_error(int err); // Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. // Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES typedef unsigned char Byte; typedef unsigned int uInt; typedef mz_ulong uLong; typedef Byte Bytef; typedef uInt uIntf; typedef char charf; typedef int intf; typedef void *voidpf; typedef uLong uLongf; typedef void *voidp; typedef void *const voidpc; #define Z_NULL 0 #define Z_NO_FLUSH MZ_NO_FLUSH #define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH #define Z_SYNC_FLUSH MZ_SYNC_FLUSH #define Z_FULL_FLUSH MZ_FULL_FLUSH #define Z_FINISH MZ_FINISH #define Z_BLOCK MZ_BLOCK #define Z_OK MZ_OK #define Z_STREAM_END MZ_STREAM_END #define Z_NEED_DICT MZ_NEED_DICT #define Z_ERRNO MZ_ERRNO #define Z_STREAM_ERROR MZ_STREAM_ERROR #define Z_DATA_ERROR MZ_DATA_ERROR #define Z_MEM_ERROR MZ_MEM_ERROR #define Z_BUF_ERROR MZ_BUF_ERROR #define Z_VERSION_ERROR MZ_VERSION_ERROR #define Z_PARAM_ERROR MZ_PARAM_ERROR #define Z_NO_COMPRESSION MZ_NO_COMPRESSION #define Z_BEST_SPEED MZ_BEST_SPEED #define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION #define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION #define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY #define Z_FILTERED MZ_FILTERED #define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY #define Z_RLE MZ_RLE #define Z_FIXED MZ_FIXED #define Z_DEFLATED MZ_DEFLATED #define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS #define alloc_func mz_alloc_func #define free_func mz_free_func #define internal_state mz_internal_state #define z_stream mz_stream #define deflateInit mz_deflateInit #define deflateInit2 mz_deflateInit2 #define deflateReset mz_deflateReset #define deflate mz_deflate #define deflateEnd mz_deflateEnd #define deflateBound mz_deflateBound #define compress mz_compress #define compress2 mz_compress2 #define compressBound mz_compressBound #define inflateInit mz_inflateInit #define inflateInit2 mz_inflateInit2 #define inflate mz_inflate #define inflateEnd mz_inflateEnd #define uncompress mz_uncompress #define crc32 mz_crc32 #define adler32 mz_adler32 #define MAX_WBITS 15 #define MAX_MEM_LEVEL 9 #define zError mz_error #define ZLIB_VERSION MZ_VERSION #define ZLIB_VERNUM MZ_VERNUM #define ZLIB_VER_MAJOR MZ_VER_MAJOR #define ZLIB_VER_MINOR MZ_VER_MINOR #define ZLIB_VER_REVISION MZ_VER_REVISION #define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION #define zlibVersion mz_version #define zlib_version mz_version() #endif // #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES #endif // MINIZ_NO_ZLIB_APIS // ------------------- Types and macros typedef unsigned char mz_uint8; typedef signed short mz_int16; typedef unsigned short mz_uint16; typedef unsigned int mz_uint32; typedef unsigned int mz_uint; typedef long long mz_int64; typedef unsigned long long mz_uint64; typedef int mz_bool; #define MZ_FALSE (0) #define MZ_TRUE (1) // An attempt to work around MSVC's spammy "warning C4127: conditional expression is constant" message. #ifdef _MSC_VER #define MZ_MACRO_END while (0, 0) #else #define MZ_MACRO_END while (0) #endif // ------------------- ZIP archive reading/writing #ifndef MINIZ_NO_ARCHIVE_APIS enum { MZ_ZIP_MAX_IO_BUF_SIZE = 64*1024, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 260, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 256 }; typedef struct { mz_uint32 m_file_index; mz_uint32 m_central_dir_ofs; mz_uint16 m_version_made_by; mz_uint16 m_version_needed; mz_uint16 m_bit_flag; mz_uint16 m_method; #ifndef MINIZ_NO_TIME time_t m_time; #endif mz_uint32 m_crc32; mz_uint64 m_comp_size; mz_uint64 m_uncomp_size; mz_uint16 m_internal_attr; mz_uint32 m_external_attr; mz_uint64 m_local_header_ofs; mz_uint32 m_comment_size; char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; } mz_zip_archive_file_stat; typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); struct mz_zip_internal_state_tag; typedef struct mz_zip_internal_state_tag mz_zip_internal_state; typedef enum { MZ_ZIP_MODE_INVALID = 0, MZ_ZIP_MODE_READING = 1, MZ_ZIP_MODE_WRITING = 2, MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 } mz_zip_mode; typedef struct mz_zip_archive_tag { mz_uint64 m_archive_size; mz_uint64 m_central_directory_file_ofs; mz_uint m_total_files; mz_zip_mode m_zip_mode; mz_uint m_file_offset_alignment; mz_alloc_func m_pAlloc; mz_free_func m_pFree; mz_realloc_func m_pRealloc; void *m_pAlloc_opaque; mz_file_read_func m_pRead; mz_file_write_func m_pWrite; void *m_pIO_opaque; mz_zip_internal_state *m_pState; } mz_zip_archive; typedef enum { MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800 } mz_zip_flags; // ZIP archive reading // Inits a ZIP archive reader. // These functions read and validate the archive's central directory. mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags); mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags); #ifndef MINIZ_NO_STDIO mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); #endif // Returns the total number of files in the archive. mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); // Returns detailed information about an archive file entry. mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); // Determines if an archive file entry is a directory entry. mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); // Retrieves the filename of an archive file entry. // Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); // Attempts to locates a file in the archive's central directory. // Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH // Returns -1 if the file cannot be found. int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); // Extracts a archive file to a memory buffer using no memory allocation. mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); // Extracts a archive file to a memory buffer. mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); // Extracts a archive file to a dynamically allocated heap buffer. void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); // Extracts a archive file using a callback function to output the file's data. mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); #ifndef MINIZ_NO_STDIO // Extracts a archive file to a disk file and sets its last accessed and modified times. // This function only extracts files, not archive directory records. mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); #endif // Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. mz_bool mz_zip_reader_end(mz_zip_archive *pZip); // ZIP archive writing #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS // Inits a ZIP archive writer. mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); #ifndef MINIZ_NO_STDIO mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); #endif // Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. // For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. // For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). // Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. // Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before // the archive is finalized the file's central directory will be hosed. mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); // Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. // To add a directory entry, call this method with an archive name ending in a forwardslash with empty buffer. // level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); #ifndef MINIZ_NO_STDIO // Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. // level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); #endif // Adds a file to an archive by fully cloning the data from another archive. // This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data, and comment fields. mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index); // Finalizes the archive by writing the central directory records followed by the end of central directory record. // After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). // An archive must be manually finalized by calling this function for it to be valid. mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize); // Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. // Note for the archive to be valid, it must have been finalized before ending. mz_bool mz_zip_writer_end(mz_zip_archive *pZip); // Misc. high-level helper functions: // mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. // level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); // Reads a single file from an archive into a heap block. // Returns NULL on failure. void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags); #endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS #endif // #ifndef MINIZ_NO_ARCHIVE_APIS // ------------------- Low-level Decompression API Definitions // Decompression flags used by tinfl_decompress(). // TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. // TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. // TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). // TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. enum { TINFL_FLAG_PARSE_ZLIB_HEADER = 1, TINFL_FLAG_HAS_MORE_INPUT = 2, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, TINFL_FLAG_COMPUTE_ADLER32 = 8 }; // High level decompression functions: // tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). // On entry: // pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. // On return: // Function returns a pointer to the decompressed data, or NULL on failure. // *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. // The caller must call mz_free() on the returned block when it's no longer needed. void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); // tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. // Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. #define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); // tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. // Returns 1 on success or 0 on failure. typedef int (*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; // Max size of LZ dictionary. #define TINFL_LZ_DICT_SIZE 32768 // Return status. typedef enum { TINFL_STATUS_BAD_PARAM = -3, TINFL_STATUS_ADLER32_MISMATCH = -2, TINFL_STATUS_FAILED = -1, TINFL_STATUS_DONE = 0, TINFL_STATUS_NEEDS_MORE_INPUT = 1, TINFL_STATUS_HAS_MORE_OUTPUT = 2 } tinfl_status; // Initializes the decompressor to its initial state. #define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END #define tinfl_get_adler32(r) (r)->m_check_adler32 // Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. // This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); // Internal/private bits follow. enum { TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19, TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS }; typedef struct { mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; } tinfl_huff_table; #if MINIZ_HAS_64BIT_REGISTERS #define TINFL_USE_64BIT_BITBUF 1 #endif #if TINFL_USE_64BIT_BITBUF typedef mz_uint64 tinfl_bit_buf_t; #define TINFL_BITBUF_SIZE (64) #else typedef mz_uint32 tinfl_bit_buf_t; #define TINFL_BITBUF_SIZE (32) #endif struct tinfl_decompressor_tag { mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; tinfl_bit_buf_t m_bit_buf; size_t m_dist_from_out_buf_start; tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; }; // ------------------- Low-level Compression API Definitions // Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). #define TDEFL_LESS_MEMORY 0 // tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): // TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). enum { TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF }; // TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. // TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). // TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. // TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). // TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) // TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. // TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. // TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. // The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). enum { TDEFL_WRITE_ZLIB_HEADER = 0x01000, TDEFL_COMPUTE_ADLER32 = 0x02000, TDEFL_GREEDY_PARSING_FLAG = 0x04000, TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, TDEFL_RLE_MATCHES = 0x10000, TDEFL_FILTER_MATCHES = 0x20000, TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 }; // High level compression functions: // tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). // On entry: // pSrc_buf, src_buf_len: Pointer and size of source block to compress. // flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. // On return: // Function returns a pointer to the compressed data, or NULL on failure. // *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. // The caller must free() the returned block when it's no longer needed. void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); // tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. // Returns 0 on failure. size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); // Compresses an image to a compressed PNG file in memory. // On entry: // pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. // The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. // level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL // If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). // On return: // Function returns a pointer to the compressed data, or NULL on failure. // *pLen_out will be set to the size of the PNG image file. // The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); // Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. typedef mz_bool (*tdefl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); // tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = 32768, TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 }; // TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). #if TDEFL_LESS_MEMORY enum { TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; #else enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; #endif // The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. typedef enum { TDEFL_STATUS_BAD_PARAM = -2, TDEFL_STATUS_PUT_BUF_FAILED = -1, TDEFL_STATUS_OKAY = 0, TDEFL_STATUS_DONE = 1, } tdefl_status; // Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums typedef enum { TDEFL_NO_FLUSH = 0, TDEFL_SYNC_FLUSH = 2, TDEFL_FULL_FLUSH = 3, TDEFL_FINISH = 4 } tdefl_flush; // tdefl's compression state structure. typedef struct { tdefl_put_buf_func_ptr m_pPut_buf_func; void *m_pPut_buf_user; mz_uint m_flags, m_max_probes[2]; int m_greedy_parsing; mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; tdefl_status m_prev_return_status; const void *m_pIn_buf; void *m_pOut_buf; size_t *m_pIn_buf_size, *m_pOut_buf_size; tdefl_flush m_flush; const mz_uint8 *m_pSrc; size_t m_src_buf_left, m_out_buf_ofs; mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; } tdefl_compressor; // Initializes the compressor. // There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. // pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. // If pBut_buf_func is NULL the user should always call the tdefl_compress() API. // flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); // Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); // tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. // tdefl_compress_buffer() always consumes the entire input buffer. tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); mz_uint32 tdefl_get_adler32(tdefl_compressor *d); // Can't use tdefl_create_comp_flags_from_zip_params if MINIZ_NO_ZLIB_APIS isn't defined, because it uses some of its macros. #ifndef MINIZ_NO_ZLIB_APIS // Create tdefl_compress() flags given zlib-style compression parameters. // level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) // window_bits may be -15 (raw deflate) or 15 (zlib) // strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); #endif // #ifndef MINIZ_NO_ZLIB_APIS #ifdef __cplusplus } #endif #endif // MINIZ_HEADER_INCLUDED // ------------------- End of Header: Implementation follows. (If you only want the header, define MINIZ_HEADER_FILE_ONLY.) #ifndef MINIZ_HEADER_FILE_ONLY typedef unsigned char mz_validate_uint16[sizeof(mz_uint16)==2 ? 1 : -1]; typedef unsigned char mz_validate_uint32[sizeof(mz_uint32)==4 ? 1 : -1]; typedef unsigned char mz_validate_uint64[sizeof(mz_uint64)==8 ? 1 : -1]; #include #include #define MZ_ASSERT(x) assert(x) #ifdef MINIZ_NO_MALLOC #define MZ_MALLOC(x) NULL #define MZ_FREE(x) (void)x, ((void)0) #define MZ_REALLOC(p, x) NULL #else #define MZ_MALLOC(x) malloc(x) #define MZ_FREE(x) free(x) #define MZ_REALLOC(p, x) realloc(p, x) #endif #define MZ_MAX(a,b) (((a)>(b))?(a):(b)) #define MZ_MIN(a,b) (((a)<(b))?(a):(b)) #define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) #define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) #else #define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) #define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) #endif #ifdef _MSC_VER #define MZ_FORCEINLINE __forceinline #elif defined(__GNUC__) #define MZ_FORCEINLINE inline __attribute__((__always_inline__)) #else #define MZ_FORCEINLINE inline #endif #ifdef __cplusplus extern "C" { #endif // ------------------- zlib-style API's mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) { mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552; if (!ptr) return MZ_ADLER32_INIT; while (buf_len) { for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; } for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; } return (s2 << 16) + s1; } // Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) { static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; mz_uint32 crcu32 = (mz_uint32)crc; if (!ptr) return MZ_CRC32_INIT; crcu32 = ~crcu32; while (buf_len--) { mz_uint8 b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; } return ~crcu32; } void mz_free(void *p) { MZ_FREE(p); } #ifndef MINIZ_NO_ZLIB_APIS static void *def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); } static void def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); } static void *def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); } const char *mz_version(void) { return MZ_VERSION; } int mz_deflateInit(mz_streamp pStream, int level) { return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); } int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) { tdefl_compressor *pComp; mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); if (!pStream) return MZ_STREAM_ERROR; if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) return MZ_PARAM_ERROR; pStream->data_type = 0; pStream->adler = MZ_ADLER32_INIT; pStream->msg = NULL; pStream->reserved = 0; pStream->total_in = 0; pStream->total_out = 0; if (!pStream->zalloc) pStream->zalloc = def_alloc_func; if (!pStream->zfree) pStream->zfree = def_free_func; pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); if (!pComp) return MZ_MEM_ERROR; pStream->state = (struct mz_internal_state *)pComp; if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) { mz_deflateEnd(pStream); return MZ_PARAM_ERROR; } return MZ_OK; } int mz_deflateReset(mz_streamp pStream) { if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) return MZ_STREAM_ERROR; pStream->total_in = pStream->total_out = 0; tdefl_init((tdefl_compressor*)pStream->state, NULL, NULL, ((tdefl_compressor*)pStream->state)->m_flags); return MZ_OK; } int mz_deflate(mz_streamp pStream, int flush) { size_t in_bytes, out_bytes; mz_ulong orig_total_in, orig_total_out; int mz_status = MZ_OK; if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) return MZ_STREAM_ERROR; if (!pStream->avail_out) return MZ_BUF_ERROR; if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; if (((tdefl_compressor*)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; orig_total_in = pStream->total_in; orig_total_out = pStream->total_out; for ( ; ; ) { tdefl_status defl_status; in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; defl_status = tdefl_compress((tdefl_compressor*)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tdefl_get_adler32((tdefl_compressor*)pStream->state); pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; if (defl_status < 0) { mz_status = MZ_STREAM_ERROR; break; } else if (defl_status == TDEFL_STATUS_DONE) { mz_status = MZ_STREAM_END; break; } else if (!pStream->avail_out) break; else if ((!pStream->avail_in) && (flush != MZ_FINISH)) { if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) break; return MZ_BUF_ERROR; // Can't make forward progress without some input. } } return mz_status; } int mz_deflateEnd(mz_streamp pStream) { if (!pStream) return MZ_STREAM_ERROR; if (pStream->state) { pStream->zfree(pStream->opaque, pStream->state); pStream->state = NULL; } return MZ_OK; } mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) { (void)pStream; // This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); } int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) { int status; mz_stream stream; memset(&stream, 0, sizeof(stream)); // In case mz_ulong is 64-bits (argh I hate longs). if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; stream.next_in = pSource; stream.avail_in = (mz_uint32)source_len; stream.next_out = pDest; stream.avail_out = (mz_uint32)*pDest_len; status = mz_deflateInit(&stream, level); if (status != MZ_OK) return status; status = mz_deflate(&stream, MZ_FINISH); if (status != MZ_STREAM_END) { mz_deflateEnd(&stream); return (status == MZ_OK) ? MZ_BUF_ERROR : status; } *pDest_len = stream.total_out; return mz_deflateEnd(&stream); } int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) { return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); } mz_ulong mz_compressBound(mz_ulong source_len) { return mz_deflateBound(NULL, source_len); } typedef struct { tinfl_decompressor m_decomp; mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits; mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; tinfl_status m_last_status; } inflate_state; int mz_inflateInit2(mz_streamp pStream, int window_bits) { inflate_state *pDecomp; if (!pStream) return MZ_STREAM_ERROR; if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR; pStream->data_type = 0; pStream->adler = 0; pStream->msg = NULL; pStream->total_in = 0; pStream->total_out = 0; pStream->reserved = 0; if (!pStream->zalloc) pStream->zalloc = def_alloc_func; if (!pStream->zfree) pStream->zfree = def_free_func; pDecomp = (inflate_state*)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); if (!pDecomp) return MZ_MEM_ERROR; pStream->state = (struct mz_internal_state *)pDecomp; tinfl_init(&pDecomp->m_decomp); pDecomp->m_dict_ofs = 0; pDecomp->m_dict_avail = 0; pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; pDecomp->m_first_call = 1; pDecomp->m_has_flushed = 0; pDecomp->m_window_bits = window_bits; return MZ_OK; } int mz_inflateInit(mz_streamp pStream) { return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); } int mz_inflate(mz_streamp pStream, int flush) { inflate_state* pState; mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; size_t in_bytes, out_bytes, orig_avail_in; tinfl_status status; if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR; if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; pState = (inflate_state*)pStream->state; if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; orig_avail_in = pStream->avail_in; first_call = pState->m_first_call; pState->m_first_call = 0; if (pState->m_last_status < 0) return MZ_DATA_ERROR; if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; pState->m_has_flushed |= (flush == MZ_FINISH); if ((flush == MZ_FINISH) && (first_call)) { // MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); pState->m_last_status = status; pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; if (status < 0) return MZ_DATA_ERROR; else if (status != TINFL_STATUS_DONE) { pState->m_last_status = TINFL_STATUS_FAILED; return MZ_BUF_ERROR; } return MZ_STREAM_END; } // flush != MZ_FINISH then we must assume there's more input. if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; if (pState->m_dict_avail) { n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; } for ( ; ; ) { in_bytes = pStream->avail_in; out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); pState->m_last_status = status; pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); pState->m_dict_avail = (mz_uint)out_bytes; n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); if (status < 0) return MZ_DATA_ERROR; // Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) return MZ_BUF_ERROR; // Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. else if (flush == MZ_FINISH) { // The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. if (status == TINFL_STATUS_DONE) return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; // status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. else if (!pStream->avail_out) return MZ_BUF_ERROR; } else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) break; } return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; } int mz_inflateEnd(mz_streamp pStream) { if (!pStream) return MZ_STREAM_ERROR; if (pStream->state) { pStream->zfree(pStream->opaque, pStream->state); pStream->state = NULL; } return MZ_OK; } int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) { mz_stream stream; int status; memset(&stream, 0, sizeof(stream)); // In case mz_ulong is 64-bits (argh I hate longs). if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; stream.next_in = pSource; stream.avail_in = (mz_uint32)source_len; stream.next_out = pDest; stream.avail_out = (mz_uint32)*pDest_len; status = mz_inflateInit(&stream); if (status != MZ_OK) return status; status = mz_inflate(&stream, MZ_FINISH); if (status != MZ_STREAM_END) { mz_inflateEnd(&stream); return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; } *pDest_len = stream.total_out; return mz_inflateEnd(&stream); } const char *mz_error(int err) { static struct { int m_err; const char *m_pDesc; } s_error_descs[] = { { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } }; mz_uint i; for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) if (s_error_descs[i].m_err == err) return s_error_descs[i].m_pDesc; return NULL; } #endif //MINIZ_NO_ZLIB_APIS // ------------------- Low-level Decompression (completely independent from all compression API's) #define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) #define TINFL_MEMSET(p, c, l) memset(p, c, l) #define TINFL_CR_BEGIN switch(r->m_state) { case 0: #define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END #define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END #define TINFL_CR_FINISH } // TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never // reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario. #define TINFL_GET_BYTE(state_index, c) do { \ if (pIn_buf_cur >= pIn_buf_end) { \ for ( ; ; ) { \ if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \ TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \ if (pIn_buf_cur < pIn_buf_end) { \ c = *pIn_buf_cur++; \ break; \ } \ } else { \ c = 0; \ break; \ } \ } \ } else c = *pIn_buf_cur++; } MZ_MACRO_END #define TINFL_NEED_BITS(state_index, n) do { mz_uint c; TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (mz_uint)(n)) #define TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END #define TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END // TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. // It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a // Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the // bit buffer contains >=15 bits (deflate's max. Huffman code size). #define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ do { \ temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ if (temp >= 0) { \ code_len = temp >> 9; \ if ((code_len) && (num_bits >= code_len)) \ break; \ } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \ code_len = TINFL_FAST_LOOKUP_BITS; \ do { \ temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ } while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \ } TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \ } while (num_bits < 15); // TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read // beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully // decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. // The slow path is only executed at the very end of the input buffer. #define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \ int temp; mz_uint code_len, c; \ if (num_bits < 15) { \ if ((pIn_buf_end - pIn_buf_cur) < 2) { \ TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ } else { \ bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \ } \ } \ if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ code_len = temp >> 9, temp &= 511; \ else { \ code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \ } sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) { static const int s_length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 }; static const int s_length_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; static const int s_dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; static const int s_dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; static const mz_uint8 s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; static const int s_min_table_sizes[3] = { 257, 1, 4 }; tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; // Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; } num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start; TINFL_CR_BEGIN bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1; if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1); counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); } } do { TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1; if (r->m_type == 0) { TINFL_SKIP_BITS(5, num_bits & 7); for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); } if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); } while ((counter) && (num_bits)) { TINFL_GET_BITS(51, dist, 8); while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = (mz_uint8)dist; counter--; } while (counter) { size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); } while (pIn_buf_cur >= pIn_buf_end) { if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT); } else { TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED); } } n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n; } } else if (r->m_type == 3) { TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); } else { if (r->m_type == 1) { mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i; r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); for ( i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8; } else { for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; } r->m_table_sizes[2] = 19; } for ( ; (int)r->m_type >= 0; r->m_type--) { int tree_next, tree_cur; tinfl_huff_table *pTable; mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree); for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++; used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); } if ((65536 != total) && (used_syms > 1)) { TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); } for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) { mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue; cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1); if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; } if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) { tree_cur -= ((rev_code >>= 1) & 1); if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1]; } tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; } if (r->m_type == 2) { for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); ) { mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; } if ((dist == 16) && (!counter)) { TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); } num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16]; TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s; } if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) { TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); } TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); } } for ( ; ; ) { mz_uint8 *pSrc; for ( ; ; ) { if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) { TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); if (counter >= 256) break; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = (mz_uint8)counter; } else { int sym2; mz_uint code_len; #if TINFL_USE_64BIT_BITBUF if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; } #else if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } #endif if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } counter = sym2; bit_buf >>= code_len; num_bits -= code_len; if (counter & 256) break; #if !TINFL_USE_64BIT_BITBUF if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } #endif if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } bit_buf >>= code_len; num_bits -= code_len; pOut_buf_cur[0] = (mz_uint8)counter; if (sym2 & 256) { pOut_buf_cur++; counter = sym2; break; } pOut_buf_cur[1] = (mz_uint8)sym2; pOut_buf_cur += 2; } } if ((counter &= 511) == 256) break; num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257]; if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; } TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; } dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) { TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); } pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) { while (counter--) { while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; } continue; } #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES else if ((counter >= 9) && (counter <= dist)) { const mz_uint8 *pSrc_end = pSrc + (counter & ~7); do { ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; pOut_buf_cur += 8; } while ((pSrc += 8) < pSrc_end); if ((counter &= 7) < 3) { if (counter) { pOut_buf_cur[0] = pSrc[0]; if (counter > 1) pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur += counter; } continue; } } #endif do { pOut_buf_cur[0] = pSrc[0]; pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur[2] = pSrc[2]; pOut_buf_cur += 3; pSrc += 3; } while ((int)(counter -= 3) > 2); if ((int)counter > 0) { pOut_buf_cur[0] = pSrc[0]; if ((int)counter > 1) pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur += counter; } } } } while (!(r->m_final & 1)); if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; } } TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); TINFL_CR_FINISH common_exit: r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start; *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next; if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) { const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size; mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552; while (buf_len) { for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; } for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; } r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH; } return status; } // Higher level helper functions. void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) { tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0; *pOut_len = 0; tinfl_init(&decomp); for ( ; ; ) { size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8*)pBuf, pBuf ? (mz_uint8*)pBuf + *pOut_len : NULL, &dst_buf_size, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) { MZ_FREE(pBuf); *pOut_len = 0; return NULL; } src_buf_ofs += src_buf_size; *pOut_len += dst_buf_size; if (status == TINFL_STATUS_DONE) break; new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128; pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); if (!pNew_buf) { MZ_FREE(pBuf); *pOut_len = 0; return NULL; } pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity; } return pBuf; } size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) { tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp); status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf, &src_buf_len, (mz_uint8*)pOut_buf, (mz_uint8*)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; } int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { int result = 0; tinfl_decompressor decomp; mz_uint8 *pDict = (mz_uint8*)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0; if (!pDict) return TINFL_STATUS_FAILED; tinfl_init(&decomp); for ( ; ; ) { size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); in_buf_ofs += in_buf_size; if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) break; if (status != TINFL_STATUS_HAS_MORE_OUTPUT) { result = (status == TINFL_STATUS_DONE); break; } dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); } MZ_FREE(pDict); *pIn_buf_size = in_buf_ofs; return result; } // ------------------- Low-level Compression (independent from all decompression API's) // Purposely making these tables static for faster init and thread safety. static const mz_uint16 s_tdefl_len_sym[256] = { 257,258,259,260,261,262,263,264,265,265,266,266,267,267,268,268,269,269,269,269,270,270,270,270,271,271,271,271,272,272,272,272, 273,273,273,273,273,273,273,273,274,274,274,274,274,274,274,274,275,275,275,275,275,275,275,275,276,276,276,276,276,276,276,276, 277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278, 279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280, 281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281, 282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282, 283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283, 284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,285 }; static const mz_uint8 s_tdefl_len_extra[256] = { 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0 }; static const mz_uint8 s_tdefl_small_dist_sym[512] = { 0,1,2,3,4,4,5,5,6,6,6,6,7,7,7,7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11, 11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14, 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, 14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 }; static const mz_uint8 s_tdefl_small_dist_extra[512] = { 0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5, 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7 }; static const mz_uint8 s_tdefl_large_dist_sym[128] = { 0,0,18,19,20,20,21,21,22,22,22,22,23,23,23,23,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26, 26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28, 28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 }; static const mz_uint8 s_tdefl_large_dist_extra[128] = { 0,0,8,8,9,9,9,9,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 }; // Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq; static tdefl_sym_freq* tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq* pSyms0, tdefl_sym_freq* pSyms1) { mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq* pCur_syms = pSyms0, *pNew_syms = pSyms1; MZ_CLEAR_OBJ(hist); for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; } while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--; for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) { const mz_uint32* pHist = &hist[pass << 8]; mz_uint offsets[256], cur_ofs = 0; for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; } for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; { tdefl_sym_freq* t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; } } return pCur_syms; } // tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) { int root, leaf, next, avbl, used, dpth; if (n==0) return; else if (n==1) { A[0].m_key = 1; return; } A[0].m_key += A[1].m_key; root = 0; leaf = 2; for (next=1; next < n-1; next++) { if (leaf>=n || A[root].m_key=n || (root=0; next--) A[next].m_key = A[A[next].m_key].m_key+1; avbl = 1; used = dpth = 0; root = n-2; next = n-1; while (avbl>0) { while (root>=0 && (int)A[root].m_key==dpth) { used++; root--; } while (avbl>used) { A[next--].m_key = (mz_uint16)(dpth); avbl--; } avbl = 2*used; dpth++; used = 0; } } // Limits canonical Huffman code table's max code size. enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 }; static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) { int i; mz_uint32 total = 0; if (code_list_len <= 1) return; for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i]; for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); while (total != (1UL << max_code_size)) { pNum_codes[max_code_size]--; for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; } total--; } } static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) { int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; MZ_CLEAR_OBJ(num_codes); if (static_table) { for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++; } else { tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; int num_used_syms = 0; const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; for (i = 0; i < table_len; i++) if (pSym_count[i]) { syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; syms0[num_used_syms++].m_sym_index = (mz_uint16)i; } pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++; tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); for (i = 1, j = num_used_syms; i <= code_size_limit; i++) for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); } next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1); for (i = 0; i < table_len; i++) { mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue; code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1); d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; } } #define TDEFL_PUT_BITS(b, l) do { \ mz_uint bits = b; mz_uint len = l; MZ_ASSERT(bits <= ((1U << len) - 1U)); \ d->m_bit_buffer |= (bits << d->m_bits_in); d->m_bits_in += len; \ while (d->m_bits_in >= 8) { \ if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ d->m_bit_buffer >>= 8; \ d->m_bits_in -= 8; \ } \ } MZ_MACRO_END #define TDEFL_RLE_PREV_CODE_SIZE() { if (rle_repeat_count) { \ if (rle_repeat_count < 3) { \ d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ while (rle_repeat_count--) packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ } else { \ d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); packed_code_sizes[num_packed_code_sizes++] = 16; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ } rle_repeat_count = 0; } } #define TDEFL_RLE_ZERO_CODE_SIZE() { if (rle_z_count) { \ if (rle_z_count < 3) { \ d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); while (rle_z_count--) packed_code_sizes[num_packed_code_sizes++] = 0; \ } else if (rle_z_count <= 10) { \ d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); packed_code_sizes[num_packed_code_sizes++] = 17; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ } else { \ d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); packed_code_sizes[num_packed_code_sizes++] = 18; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ } rle_z_count = 0; } } static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; static void tdefl_start_dynamic_block(tdefl_compressor *d) { int num_lit_codes, num_dist_codes, num_bit_lengths; mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; d->m_huff_count[0][256] = 1; tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break; for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break; memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0; memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); for (i = 0; i < total_code_sizes_to_pack; i++) { mz_uint8 code_size = code_sizes_to_pack[i]; if (!code_size) { TDEFL_RLE_PREV_CODE_SIZE(); if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); } } else { TDEFL_RLE_ZERO_CODE_SIZE(); if (code_size != prev_code_size) { TDEFL_RLE_PREV_CODE_SIZE(); d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); packed_code_sizes[num_packed_code_sizes++] = code_size; } else if (++rle_repeat_count == 6) { TDEFL_RLE_PREV_CODE_SIZE(); } } prev_code_size = code_size; } if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } else { TDEFL_RLE_ZERO_CODE_SIZE(); } tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); TDEFL_PUT_BITS(2, 2); TDEFL_PUT_BITS(num_lit_codes - 257, 5); TDEFL_PUT_BITS(num_dist_codes - 1, 5); for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) break; num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4); for (i = 0; (int)i < num_bit_lengths; i++) TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes; ) { mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); } } static void tdefl_start_static_block(tdefl_compressor *d) { mz_uint i; mz_uint8 *p = &d->m_huff_code_sizes[0][0]; for (i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8; memset(d->m_huff_code_sizes[1], 5, 32); tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); TDEFL_PUT_BITS(1, 2); } static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { mz_uint flags; mz_uint8 *pLZ_codes; mz_uint8 *pOutput_buf = d->m_pOutput_buf; mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; mz_uint64 bit_buffer = d->m_bit_buffer; mz_uint bits_in = d->m_bits_in; #define TDEFL_PUT_BITS_FAST(b, l) { bit_buffer |= (((mz_uint64)(b)) << bits_in); bits_in += (l); } flags = 1; for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) { if (flags == 1) flags = *pLZ_codes++ | 0x100; if (flags & 1) { mz_uint s0, s1, n0, n1, sym, num_extra_bits; mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); pLZ_codes += 3; MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); // This sequence coaxes MSVC into using cmov's vs. jmp's. s0 = s_tdefl_small_dist_sym[match_dist & 511]; n0 = s_tdefl_small_dist_extra[match_dist & 511]; s1 = s_tdefl_large_dist_sym[match_dist >> 8]; n1 = s_tdefl_large_dist_extra[match_dist >> 8]; sym = (match_dist < 512) ? s0 : s1; num_extra_bits = (match_dist < 512) ? n0 : n1; MZ_ASSERT(d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); } else { mz_uint lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { flags >>= 1; lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { flags >>= 1; lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); } } } if (pOutput_buf >= d->m_pOutput_buf_end) return MZ_FALSE; *(mz_uint64*)pOutput_buf = bit_buffer; pOutput_buf += (bits_in >> 3); bit_buffer >>= (bits_in & ~7); bits_in &= 7; } #undef TDEFL_PUT_BITS_FAST d->m_pOutput_buf = pOutput_buf; d->m_bits_in = 0; d->m_bit_buffer = 0; while (bits_in) { mz_uint32 n = MZ_MIN(bits_in, 16); TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); bit_buffer >>= n; bits_in -= n; } TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); return (d->m_pOutput_buf < d->m_pOutput_buf_end); } #else static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { mz_uint flags; mz_uint8 *pLZ_codes; flags = 1; for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) { if (flags == 1) flags = *pLZ_codes++ | 0x100; if (flags & 1) { mz_uint sym, num_extra_bits; mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3; MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); if (match_dist < 512) { sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist]; } else { sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; } MZ_ASSERT(d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); } else { mz_uint lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); } } TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); return (d->m_pOutput_buf < d->m_pOutput_buf_end); } #endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) { if (static_block) tdefl_start_static_block(d); else tdefl_start_dynamic_block(d); return tdefl_compress_lz_codes(d); } static int tdefl_flush_block(tdefl_compressor *d, int flush) { mz_uint saved_bit_buf, saved_bits_in; mz_uint8 *pSaved_output_buf; mz_bool comp_block_succeeded = MZ_FALSE; int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; d->m_pOutput_buf = pOutput_buf_start; d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; MZ_ASSERT(!d->m_output_flush_remaining); d->m_output_flush_ofs = 0; d->m_output_flush_remaining = 0; *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) { TDEFL_PUT_BITS(0x78, 8); TDEFL_PUT_BITS(0x01, 8); } TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in; if (!use_raw_block) comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); // If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. if ( ((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size) ) { mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; TDEFL_PUT_BITS(0, 2); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) { TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); } for (i = 0; i < d->m_total_lz_bytes; ++i) { TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); } } // Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. else if (!comp_block_succeeded) { d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; tdefl_compress_block(d, MZ_TRUE); } if (flush) { if (flush == TDEFL_FINISH) { if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { mz_uint i, a = d->m_adler32; for (i = 0; i < 4; i++) { TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); a <<= 8; } } } else { mz_uint i, z = 0; TDEFL_PUT_BITS(0, 3); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, z ^= 0xFFFF) { TDEFL_PUT_BITS(z & 0xFFFF, 16); } } } MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; d->m_total_lz_bytes = 0; d->m_block_index++; if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) { if (d->m_pPut_buf_func) { *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); } else if (pOutput_buf_start == d->m_output_buf) { int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); d->m_out_buf_ofs += bytes_to_copy; if ((n -= bytes_to_copy) != 0) { d->m_output_flush_ofs = bytes_to_copy; d->m_output_flush_remaining = n; } } else { d->m_out_buf_ofs += n; } } return d->m_output_flush_remaining; } #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES #define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16*)(p) static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) { mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; const mz_uint16 *s = (const mz_uint16*)(d->m_dict + pos), *p, *q; mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD(s); MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; for ( ; ; ) { for ( ; ; ) { if (--num_probes_left == 0) return; #define TDEFL_PROBE \ next_probe_pos = d->m_next[probe_pos]; \ if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) break; TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; } if (!dist) break; q = (const mz_uint16*)(d->m_dict + probe_pos); if (TDEFL_READ_UNALIGNED_WORD(q) != s01) continue; p = s; probe_len = 32; do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); if (!probe_len) { *pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); break; } else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8*)p == *(const mz_uint8*)q)) > match_len) { *pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break; c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); } } } #else static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) { mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; const mz_uint8 *s = d->m_dict + pos, *p, *q; mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; for ( ; ; ) { for ( ; ; ) { if (--num_probes_left == 0) return; #define TDEFL_PROBE \ next_probe_pos = d->m_next[probe_pos]; \ if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) break; TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; } if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) if (*p++ != *q++) break; if (probe_len > match_len) { *pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return; c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1]; } } } #endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN static mz_bool tdefl_compress_fast(tdefl_compressor *d) { // Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) { const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); d->m_src_buf_left -= num_bytes_to_process; lookahead_size += num_bytes_to_process; while (num_bytes_to_process) { mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); memcpy(d->m_dict + dst_pos, d->m_pSrc, n); if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); d->m_pSrc += n; dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; num_bytes_to_process -= n; } dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break; while (lookahead_size >= 4) { mz_uint cur_match_dist, cur_match_len = 1; mz_uint8 *pCur_dict = d->m_dict + cur_pos; mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF; mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; mz_uint probe_pos = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)lookahead_pos; if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((*(const mz_uint32 *)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) { const mz_uint16 *p = (const mz_uint16 *)pCur_dict; const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); mz_uint32 probe_len = 32; do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); if (!probe_len) cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U))) { cur_match_len = 1; *pLZ_code_buf++ = (mz_uint8)first_trigram; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); d->m_huff_count[0][(mz_uint8)first_trigram]++; } else { mz_uint32 s0, s1; cur_match_len = MZ_MIN(cur_match_len, lookahead_size); MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); cur_match_dist--; pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; pLZ_code_buf += 3; *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; } } else { *pLZ_code_buf++ = (mz_uint8)first_trigram; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); d->m_huff_count[0][(mz_uint8)first_trigram]++; } if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } total_lz_bytes += cur_match_len; lookahead_pos += cur_match_len; dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE); cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; MZ_ASSERT(lookahead_size >= cur_match_len); lookahead_size -= cur_match_len; if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { int n; d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; } } while (lookahead_size) { mz_uint8 lit = d->m_dict[cur_pos]; total_lz_bytes++; *pLZ_code_buf++ = lit; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } d->m_huff_count[0][lit]++; lookahead_pos++; dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE); cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; lookahead_size--; if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { int n; d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; } } } d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; return MZ_TRUE; } #endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) { d->m_total_lz_bytes++; *d->m_pLZ_code_buf++ = lit; *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } d->m_huff_count[0][lit]++; } static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) { mz_uint32 s0, s1; MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); d->m_total_lz_bytes += match_len; d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); match_dist -= 1; d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); d->m_pLZ_code_buf += 3; *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; if (match_len >= TDEFL_MIN_MATCH_LEN) d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; } static mz_bool tdefl_compress_normal(tdefl_compressor *d) { const mz_uint8 *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left; tdefl_flush flush = d->m_flush; while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) { mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; // Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) { mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; src_buf_left -= num_bytes_to_process; d->m_lookahead_size += num_bytes_to_process; while (pSrc != pSrc_end) { mz_uint8 c = *pSrc++; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++; } } else { while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) { mz_uint8 c = *pSrc++; mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; src_buf_left--; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) { mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); } } } d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) break; // Simple lazy/greedy parsing state machine. len_to_move = 1; cur_match_dist = 0; cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) { if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) { mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; cur_match_len = 0; while (cur_match_len < d->m_lookahead_size) { if (d->m_dict[cur_pos + cur_match_len] != c) break; cur_match_len++; } if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1; } } else { tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); } if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) { cur_match_dist = cur_match_len = 0; } if (d->m_saved_match_len) { if (cur_match_len > d->m_saved_match_len) { tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); if (cur_match_len >= 128) { tdefl_record_match(d, cur_match_len, cur_match_dist); d->m_saved_match_len = 0; len_to_move = cur_match_len; } else { d->m_saved_lit = d->m_dict[cur_pos]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; } } else { tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0; } } else if (!cur_match_dist) tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) { tdefl_record_match(d, cur_match_len, cur_match_dist); len_to_move = cur_match_len; } else { d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; } // Move the lookahead forward by len_to_move bytes. d->m_lookahead_pos += len_to_move; MZ_ASSERT(d->m_lookahead_size >= len_to_move); d->m_lookahead_size -= len_to_move; d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE); // Check if it's time to flush the current LZ codes to the internal output buffer. if ( (d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || ( (d->m_total_lz_bytes > 31*1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) ) { int n; d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; } } d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; return MZ_TRUE; } static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) { if (d->m_pIn_buf_size) { *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; } if (d->m_pOut_buf_size) { size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); d->m_output_flush_ofs += (mz_uint)n; d->m_output_flush_remaining -= (mz_uint)n; d->m_out_buf_ofs += n; *d->m_pOut_buf_size = d->m_out_buf_ofs; } return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; } tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) { if (!d) { if (pIn_buf_size) *pIn_buf_size = 0; if (pOut_buf_size) *pOut_buf_size = 0; return TDEFL_STATUS_BAD_PARAM; } d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size; d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size; d->m_pSrc = (const mz_uint8 *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; d->m_out_buf_ofs = 0; d->m_flush = flush; if ( ((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf) ) { if (pIn_buf_size) *pIn_buf_size = 0; if (pOut_buf_size) *pOut_buf_size = 0; return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); } d->m_wants_to_finish |= (flush == TDEFL_FINISH); if ((d->m_output_flush_remaining) || (d->m_finished)) return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) { if (!tdefl_compress_fast(d)) return d->m_prev_return_status; } else #endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN { if (!tdefl_compress_normal(d)) return d->m_prev_return_status; } if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) { if (tdefl_flush_block(d, flush) < 0) return d->m_prev_return_status; d->m_finished = (flush == TDEFL_FINISH); if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_OBJ(d->m_hash); MZ_CLEAR_OBJ(d->m_next); d->m_dict_size = 0; } } return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); } tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) { MZ_ASSERT(d->m_pPut_buf_func); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); } tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { d->m_pPut_buf_func = pPut_buf_func; d->m_pPut_buf_user = pPut_buf_user; d->m_flags = (mz_uint)(flags); d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_hash); d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY; d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1; d->m_pIn_buf = NULL; d->m_pOut_buf = NULL; d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL; d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0; memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); return TDEFL_STATUS_OKAY; } tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) { return d->m_prev_return_status; } mz_uint32 tdefl_get_adler32(tdefl_compressor *d) { return d->m_adler32; } mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { tdefl_compressor *pComp; mz_bool succeeded; if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE; pComp = (tdefl_compressor*)MZ_MALLOC(sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE; succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); MZ_FREE(pComp); return succeeded; } typedef struct { size_t m_size, m_capacity; mz_uint8 *m_pBuf; mz_bool m_expandable; } tdefl_output_buffer; static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) { tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; size_t new_size = p->m_size + len; if (new_size > p->m_capacity) { size_t new_capacity = p->m_capacity; mz_uint8 *pNew_buf; if (!p->m_expandable) return MZ_FALSE; do { new_capacity = MZ_MAX(128U, new_capacity << 1U); } while (new_size > new_capacity); pNew_buf = (mz_uint8*)MZ_REALLOC(p->m_pBuf, new_capacity); if (!pNew_buf) return MZ_FALSE; p->m_pBuf = pNew_buf; p->m_capacity = new_capacity; } memcpy((mz_uint8*)p->m_pBuf + p->m_size, pBuf, len); p->m_size = new_size; return MZ_TRUE; } void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) { tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); if (!pOut_len) return MZ_FALSE; else *pOut_len = 0; out_buf.m_expandable = MZ_TRUE; if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return NULL; *pOut_len = out_buf.m_size; return out_buf.m_pBuf; } size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) { tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); if (!pOut_buf) return 0; out_buf.m_pBuf = (mz_uint8*)pOut_buf; out_buf.m_capacity = out_buf_len; if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return 0; return out_buf.m_size; } #ifndef MINIZ_NO_ZLIB_APIS static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; // level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) { mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER; if (!level) comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; else if (strategy == MZ_FILTERED) comp_flags |= TDEFL_FILTER_MATCHES; else if (strategy == MZ_HUFFMAN_ONLY) comp_flags &= ~TDEFL_MAX_PROBES_MASK; else if (strategy == MZ_FIXED) comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; else if (strategy == MZ_RLE) comp_flags |= TDEFL_RLE_MATCHES; return comp_flags; } #endif //MINIZ_NO_ZLIB_APIS #ifdef _MSC_VER #pragma warning (push) #pragma warning (disable:4204) // nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) #endif // Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at // http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. // This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) { // Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); tdefl_output_buffer out_buf; int i, bpl = w * num_chans, y, z; mz_uint32 c; *pLen_out = 0; if (!pComp) return NULL; MZ_CLEAR_OBJ(out_buf); out_buf.m_expandable = MZ_TRUE; out_buf.m_capacity = 57+MZ_MAX(64, (1+bpl)*h); if (NULL == (out_buf.m_pBuf = (mz_uint8*)MZ_MALLOC(out_buf.m_capacity))) { MZ_FREE(pComp); return NULL; } // write dummy header for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf); // compress image data tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8*)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); } if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } // write real header *pLen_out = out_buf.m_size-41; { static const mz_uint8 chans[] = {0x00, 0x00, 0x04, 0x02, 0x06}; mz_uint8 pnghdr[41]={0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52, 0,0,(mz_uint8)(w>>8),(mz_uint8)w,0,0,(mz_uint8)(h>>8),(mz_uint8)h,8,chans[num_chans],0,0,0,0,0,0,0, (mz_uint8)(*pLen_out>>24),(mz_uint8)(*pLen_out>>16),(mz_uint8)(*pLen_out>>8),(mz_uint8)*pLen_out,0x49,0x44,0x41,0x54}; c=(mz_uint32)mz_crc32(MZ_CRC32_INIT,pnghdr+12,17); for (i=0; i<4; ++i, c<<=8) ((mz_uint8*)(pnghdr+29))[i]=(mz_uint8)(c>>24); memcpy(out_buf.m_pBuf, pnghdr, 41); } // write footer (IDAT CRC-32, followed by IEND chunk) if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { *pLen_out = 0; MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } c = (mz_uint32)mz_crc32(MZ_CRC32_INIT,out_buf.m_pBuf+41-4, *pLen_out+4); for (i=0; i<4; ++i, c<<=8) (out_buf.m_pBuf+out_buf.m_size-16)[i] = (mz_uint8)(c >> 24); // compute final size of file, grab compressed data buffer and return *pLen_out += 57; MZ_FREE(pComp); return out_buf.m_pBuf; } void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) { // Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); } #ifdef _MSC_VER #pragma warning (pop) #endif // ------------------- .ZIP archive reading #ifndef MINIZ_NO_ARCHIVE_APIS #ifdef MINIZ_NO_STDIO #define MZ_FILE void * #else #include #include #if defined(_MSC_VER) || defined(__MINGW64__) static FILE *mz_fopen(const char *pFilename, const char *pMode) { FILE* pFile = NULL; fopen_s(&pFile, pFilename, pMode); return pFile; } static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) { FILE* pFile = NULL; if (freopen_s(&pFile, pPath, pMode, pStream)) return NULL; return pFile; } #ifndef MINIZ_NO_TIME #include #endif #define MZ_FILE FILE #define MZ_FOPEN mz_fopen #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 _ftelli64 #define MZ_FSEEK64 _fseeki64 #define MZ_FILE_STAT_STRUCT _stat #define MZ_FILE_STAT _stat #define MZ_FFLUSH fflush #define MZ_FREOPEN mz_freopen #define MZ_DELETE_FILE remove #elif defined(__MINGW32__) #ifndef MINIZ_NO_TIME #include #endif #define MZ_FILE FILE #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftello64 #define MZ_FSEEK64 fseeko64 #define MZ_FILE_STAT_STRUCT _stat #define MZ_FILE_STAT _stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #elif defined(__TINYC__) #ifndef MINIZ_NO_TIME #include #endif #define MZ_FILE FILE #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftell #define MZ_FSEEK64 fseek #define MZ_FILE_STAT_STRUCT stat #define MZ_FILE_STAT stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #elif defined(__GNUC__) && _LARGEFILE64_SOURCE #ifndef MINIZ_NO_TIME #include #endif #define MZ_FILE FILE #define MZ_FOPEN(f, m) fopen64(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftello64 #define MZ_FSEEK64 fseeko64 #define MZ_FILE_STAT_STRUCT stat64 #define MZ_FILE_STAT stat64 #define MZ_FFLUSH fflush #define MZ_FREOPEN(p, m, s) freopen64(p, m, s) #define MZ_DELETE_FILE remove #else #ifndef MINIZ_NO_TIME #include #endif #define MZ_FILE FILE #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftello #define MZ_FSEEK64 fseeko #define MZ_FILE_STAT_STRUCT stat #define MZ_FILE_STAT stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #endif // #ifdef _MSC_VER #endif // #ifdef MINIZ_NO_STDIO #define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) // Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. enum { // ZIP archive identifiers and record sizes MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, // Central directory header record offsets MZ_ZIP_CDH_SIG_OFS = 0, MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, MZ_ZIP_CDH_BIT_FLAG_OFS = 8, MZ_ZIP_CDH_METHOD_OFS = 10, MZ_ZIP_CDH_FILE_TIME_OFS = 12, MZ_ZIP_CDH_FILE_DATE_OFS = 14, MZ_ZIP_CDH_CRC32_OFS = 16, MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, MZ_ZIP_CDH_DISK_START_OFS = 34, MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, // Local directory header offsets MZ_ZIP_LDH_SIG_OFS = 0, MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, MZ_ZIP_LDH_BIT_FLAG_OFS = 6, MZ_ZIP_LDH_METHOD_OFS = 8, MZ_ZIP_LDH_FILE_TIME_OFS = 10, MZ_ZIP_LDH_FILE_DATE_OFS = 12, MZ_ZIP_LDH_CRC32_OFS = 14, MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, // End of central directory offsets MZ_ZIP_ECDH_SIG_OFS = 0, MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, }; typedef struct { void *m_p; size_t m_size, m_capacity; mz_uint m_element_size; } mz_zip_array; struct mz_zip_internal_state_tag { mz_zip_array m_central_dir; mz_zip_array m_central_dir_offsets; mz_zip_array m_sorted_central_dir_offsets; MZ_FILE *m_pFile; void *m_pMem; size_t m_mem_size; size_t m_mem_capacity; }; #define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size #define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) { pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); memset(pArray, 0, sizeof(mz_zip_array)); } static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) { void *pNew_p; size_t new_capacity = min_new_capacity; MZ_ASSERT(pArray->m_element_size); if (pArray->m_capacity >= min_new_capacity) return MZ_TRUE; if (growing) { new_capacity = MZ_MAX(1, pArray->m_capacity); while (new_capacity < min_new_capacity) new_capacity *= 2; } if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) return MZ_FALSE; pArray->m_p = pNew_p; pArray->m_capacity = new_capacity; return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) { if (new_capacity > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return MZ_FALSE; } return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) { if (new_size > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return MZ_FALSE; } pArray->m_size = new_size; return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) { return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); } static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) { size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE; memcpy((mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); return MZ_TRUE; } #ifndef MINIZ_NO_TIME static time_t mz_zip_dos_to_time_t(int dos_time, int dos_date) { struct tm tm; memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1; tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; tm.tm_mon = ((dos_date >> 5) & 15) - 1; tm.tm_mday = dos_date & 31; tm.tm_hour = (dos_time >> 11) & 31; tm.tm_min = (dos_time >> 5) & 63; tm.tm_sec = (dos_time << 1) & 62; return mktime(&tm); } static void mz_zip_time_to_dos_time(time_t time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) { #ifdef _MSC_VER struct tm tm_struct; struct tm *tm = &tm_struct; errno_t err = localtime_s(tm, &time); if (err) { *pDOS_date = 0; *pDOS_time = 0; return; } #else struct tm *tm = localtime(&time); #endif *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); } #endif #ifndef MINIZ_NO_STDIO static mz_bool mz_zip_get_file_modified_time(const char *pFilename, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) { #ifdef MINIZ_NO_TIME (void)pFilename; *pDOS_date = *pDOS_time = 0; #else struct MZ_FILE_STAT_STRUCT file_stat; // On Linux with x86 glibc, this call will fail on large files (>= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. if (MZ_FILE_STAT(pFilename, &file_stat) != 0) return MZ_FALSE; mz_zip_time_to_dos_time(file_stat.st_mtime, pDOS_time, pDOS_date); #endif // #ifdef MINIZ_NO_TIME return MZ_TRUE; } #ifndef MINIZ_NO_TIME static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time, time_t modified_time) { struct utimbuf t; t.actime = access_time; t.modtime = modified_time; return !utime(pFilename, &t); } #endif // #ifndef MINIZ_NO_TIME #endif // #ifndef MINIZ_NO_STDIO static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint32 flags) { (void)flags; if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) return MZ_FALSE; if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; if (!pZip->m_pFree) pZip->m_pFree = def_free_func; if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; pZip->m_zip_mode = MZ_ZIP_MODE_READING; pZip->m_archive_size = 0; pZip->m_central_directory_file_ofs = 0; pZip->m_total_files = 0; if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) return MZ_FALSE; memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) { const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); mz_uint8 l = 0, r = 0; pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pE = pL + MZ_MIN(l_len, r_len); while (pL < pE) { if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) break; pL++; pR++; } return (pL == pE) ? (l_len < r_len) : (l < r); } #define MZ_SWAP_UINT32(a, b) do { mz_uint32 t = a; a = b; b = t; } MZ_MACRO_END // Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) { mz_zip_internal_state *pState = pZip->m_pState; const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; const mz_zip_array *pCentral_dir = &pState->m_central_dir; mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); const int size = pZip->m_total_files; int start = (size - 2) >> 1, end; while (start >= 0) { int child, root = start; for ( ; ; ) { if ((child = (root << 1) + 1) >= size) break; child += (((child + 1) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1]))); if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) break; MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; } start--; } end = size - 1; while (end > 0) { int child, root = 0; MZ_SWAP_UINT32(pIndices[end], pIndices[0]); for ( ; ; ) { if ((child = (root << 1) + 1) >= end) break; child += (((child + 1) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1])); if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) break; MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; } end--; } } static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint32 flags) { mz_uint cdir_size, num_this_disk, cdir_disk_index; mz_uint64 cdir_ofs; mz_int64 cur_file_ofs; const mz_uint8 *p; mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32; mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); // Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return MZ_FALSE; // Find the end of central directory record by scanning the file from the end towards the beginning. cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); for ( ; ; ) { int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) return MZ_FALSE; for (i = n - 4; i >= 0; --i) if (MZ_READ_LE32(pBuf + i) == MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) break; if (i >= 0) { cur_file_ofs += i; break; } if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (0xFFFF + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) return MZ_FALSE; cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); } // Read and verify the end of central directory record. if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return MZ_FALSE; if ((MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) || ((pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS)) != MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS))) return MZ_FALSE; num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) return MZ_FALSE; if ((cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS)) < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) return MZ_FALSE; cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) return MZ_FALSE; pZip->m_central_directory_file_ofs = cdir_ofs; if (pZip->m_total_files) { mz_uint i, n; // Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and another to hold the sorted indices. if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) return MZ_FALSE; if (sort_central_dir) { if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) return MZ_FALSE; } if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) return MZ_FALSE; // Now create an index into the central directory file records, do some basic sanity checking on each record, and check for zip64 entries (which are not yet supported). p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) { mz_uint total_header_size, comp_size, decomp_size, disk_index; if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) return MZ_FALSE; MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); if (sort_central_dir) MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) || (comp_size == 0xFFFFFFFF)) return MZ_FALSE; disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); if ((disk_index != num_this_disk) && (disk_index != 1)) return MZ_FALSE; if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) return MZ_FALSE; if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) return MZ_FALSE; n -= total_header_size; p += total_header_size; } } if (sort_central_dir) mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); return MZ_TRUE; } mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags) { if ((!pZip) || (!pZip->m_pRead)) return MZ_FALSE; if (!mz_zip_reader_init_internal(pZip, flags)) return MZ_FALSE; pZip->m_archive_size = size; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end(pZip); return MZ_FALSE; } return MZ_TRUE; } static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); return s; } mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags) { if (!mz_zip_reader_init_internal(pZip, flags)) return MZ_FALSE; pZip->m_archive_size = size; pZip->m_pRead = mz_zip_mem_read_func; pZip->m_pIO_opaque = pZip; #ifdef __cplusplus pZip->m_pState->m_pMem = const_cast(pMem); #else pZip->m_pState->m_pMem = (void *)pMem; #endif pZip->m_pState->m_mem_size = size; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end(pZip); return MZ_FALSE; } return MZ_TRUE; } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) return 0; return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); } mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) { mz_uint64 file_size; MZ_FILE *pFile = MZ_FOPEN(pFilename, "rb"); if (!pFile) return MZ_FALSE; if (MZ_FSEEK64(pFile, 0, SEEK_END)) { MZ_FCLOSE(pFile); return MZ_FALSE; } file_size = MZ_FTELL64(pFile); if (!mz_zip_reader_init_internal(pZip, flags)) { MZ_FCLOSE(pFile); return MZ_FALSE; } pZip->m_pRead = mz_zip_file_read_func; pZip->m_pIO_opaque = pZip; pZip->m_pState->m_pFile = pFile; pZip->m_archive_size = file_size; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end(pZip); return MZ_FALSE; } return MZ_TRUE; } #endif // #ifndef MINIZ_NO_STDIO mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) { return pZip ? pZip->m_total_files : 0; } static MZ_FORCEINLINE const mz_uint8 *mz_zip_reader_get_cdh(mz_zip_archive *pZip, mz_uint file_index) { if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) return NULL; return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); } mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) { mz_uint m_bit_flag; const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); if (!p) return MZ_FALSE; m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); return (m_bit_flag & 1); } mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) { mz_uint filename_len, external_attr; const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); if (!p) return MZ_FALSE; // First see if the filename ends with a '/' character. filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); if (filename_len) { if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') return MZ_TRUE; } // Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. // Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. // FIXME: Remove this check? Is it necessary - we already check the filename. external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); if ((external_attr & 0x10) != 0) return MZ_TRUE; return MZ_FALSE; } mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) { mz_uint n; const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); if ((!p) || (!pStat)) return MZ_FALSE; // Unpack the central directory record. pStat->m_file_index = file_index; pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); #ifndef MINIZ_NO_TIME pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); #endif pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); // Copy as much of the filename and comment as possible. n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pStat->m_filename[n] = '\0'; n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); pStat->m_comment_size = n; memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); pStat->m_comment[n] = '\0'; return MZ_TRUE; } mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) { mz_uint n; const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); if (!p) { if (filename_buf_size) pFilename[0] = '\0'; return 0; } n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); if (filename_buf_size) { n = MZ_MIN(n, filename_buf_size - 1); memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pFilename[n] = '\0'; } return n + 1; } static MZ_FORCEINLINE mz_bool mz_zip_reader_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) { mz_uint i; if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) return 0 == memcmp(pA, pB, len); for (i = 0; i < len; ++i) if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) return MZ_FALSE; return MZ_TRUE; } static MZ_FORCEINLINE int mz_zip_reader_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) { const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); mz_uint8 l = 0, r = 0; pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pE = pL + MZ_MIN(l_len, r_len); while (pL < pE) { if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) break; pL++; pR++; } return (pL == pE) ? (int)(l_len - r_len) : (l - r); } static int mz_zip_reader_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename) { mz_zip_internal_state *pState = pZip->m_pState; const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; const mz_zip_array *pCentral_dir = &pState->m_central_dir; mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); const int size = pZip->m_total_files; const mz_uint filename_len = (mz_uint)strlen(pFilename); int l = 0, h = size - 1; while (l <= h) { int m = (l + h) >> 1, file_index = pIndices[m], comp = mz_zip_reader_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); if (!comp) return file_index; else if (comp < 0) l = m + 1; else h = m - 1; } return -1; } int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) { mz_uint file_index; size_t name_len, comment_len; if ((!pZip) || (!pZip->m_pState) || (!pName) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) return -1; if (((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) return mz_zip_reader_locate_file_binary_search(pZip, pName); name_len = strlen(pName); if (name_len > 0xFFFF) return -1; comment_len = pComment ? strlen(pComment) : 0; if (comment_len > 0xFFFF) return -1; for (file_index = 0; file_index < pZip->m_total_files; file_index++) { const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; if (filename_len < name_len) continue; if (comment_len) { mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); const char *pFile_comment = pFilename + filename_len + file_extra_len; if ((file_comment_len != comment_len) || (!mz_zip_reader_string_equal(pComment, pFile_comment, file_comment_len, flags))) continue; } if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) { int ofs = filename_len - 1; do { if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) break; } while (--ofs >= 0); ofs++; pFilename += ofs; filename_len -= ofs; } if ((filename_len == name_len) && (mz_zip_reader_string_equal(pName, pFilename, filename_len, flags))) return file_index; } return -1; } mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) { int status = TINFL_STATUS_DONE; mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; mz_zip_archive_file_stat file_stat; void *pRead_buf; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; tinfl_decompressor inflator; if ((buf_size) && (!pBuf)) return MZ_FALSE; if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes) if (!file_stat.m_comp_size) return MZ_TRUE; // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers). // I'm torn how to handle this case - should it fail instead? if (mz_zip_reader_is_file_a_directory(pZip, file_index)) return MZ_TRUE; // Encryption and patch files are not supported. if (file_stat.m_bit_flag & (1 | 32)) return MZ_FALSE; // This function only supports stored and deflate. if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) return MZ_FALSE; // Ensure supplied output buffer is large enough. needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; if (buf_size < needed_size) return MZ_FALSE; // Read and parse the local directory entry. cur_file_ofs = file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return MZ_FALSE; if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return MZ_FALSE; cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) return MZ_FALSE; if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { // The file is stored or the caller has requested the compressed data. if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) return MZ_FALSE; return ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) != 0) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) == file_stat.m_crc32); } // Decompress the file either directly from memory or from a file input buffer. tinfl_init(&inflator); if (pZip->m_pState->m_pMem) { // Read directly from the archive in memory. pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; read_buf_size = read_buf_avail = file_stat.m_comp_size; comp_remaining = 0; } else if (pUser_read_buf) { // Use a user provided read buffer. if (!user_read_buf_size) return MZ_FALSE; pRead_buf = (mz_uint8 *)pUser_read_buf; read_buf_size = user_read_buf_size; read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } else { // Temporarily allocate a read buffer. read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); #ifdef _MSC_VER if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) #else if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) #endif return MZ_FALSE; if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) return MZ_FALSE; read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } do { size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { status = TINFL_STATUS_FAILED; break; } cur_file_ofs += read_buf_avail; comp_remaining -= read_buf_avail; read_buf_ofs = 0; } in_buf_size = (size_t)read_buf_avail; status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); read_buf_avail -= in_buf_size; read_buf_ofs += in_buf_size; out_buf_ofs += out_buf_size; } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); if (status == TINFL_STATUS_DONE) { // Make sure the entire file was decompressed, and check its CRC. if ((out_buf_ofs != file_stat.m_uncomp_size) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32)) status = TINFL_STATUS_FAILED; } if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return status == TINFL_STATUS_DONE; } mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) { int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); if (file_index < 0) return MZ_FALSE; return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); } mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) { return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); } mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) { return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); } void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) { mz_uint64 comp_size, uncomp_size, alloc_size; const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); void *pBuf; if (pSize) *pSize = 0; if (!p) return NULL; comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; #ifdef _MSC_VER if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) #else if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) #endif return NULL; if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) return NULL; if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return NULL; } if (pSize) *pSize = (size_t)alloc_size; return pBuf; } void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) { int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); if (file_index < 0) { if (pSize) *pSize = 0; return MZ_FALSE; } return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); } mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) { int status = TINFL_STATUS_DONE; mz_uint file_crc32 = MZ_CRC32_INIT; mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; mz_zip_archive_file_stat file_stat; void *pRead_buf = NULL; void *pWrite_buf = NULL; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes) if (!file_stat.m_comp_size) return MZ_TRUE; // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers). // I'm torn how to handle this case - should it fail instead? if (mz_zip_reader_is_file_a_directory(pZip, file_index)) return MZ_TRUE; // Encryption and patch files are not supported. if (file_stat.m_bit_flag & (1 | 32)) return MZ_FALSE; // This function only supports stored and deflate. if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) return MZ_FALSE; // Read and parse the local directory entry. cur_file_ofs = file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return MZ_FALSE; if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return MZ_FALSE; cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) return MZ_FALSE; // Decompress the file either directly from memory or from a file input buffer. if (pZip->m_pState->m_pMem) { pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; read_buf_size = read_buf_avail = file_stat.m_comp_size; comp_remaining = 0; } else { read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) return MZ_FALSE; read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { // The file is stored or the caller has requested the compressed data. if (pZip->m_pState->m_pMem) { #ifdef _MSC_VER if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) #else if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) #endif return MZ_FALSE; if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) status = TINFL_STATUS_FAILED; else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); cur_file_ofs += file_stat.m_comp_size; out_buf_ofs += file_stat.m_comp_size; comp_remaining = 0; } else { while (comp_remaining) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { status = TINFL_STATUS_FAILED; break; } if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { status = TINFL_STATUS_FAILED; break; } cur_file_ofs += read_buf_avail; out_buf_ofs += read_buf_avail; comp_remaining -= read_buf_avail; } } } else { tinfl_decompressor inflator; tinfl_init(&inflator); if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) status = TINFL_STATUS_FAILED; else { do { mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { status = TINFL_STATUS_FAILED; break; } cur_file_ofs += read_buf_avail; comp_remaining -= read_buf_avail; read_buf_ofs = 0; } in_buf_size = (size_t)read_buf_avail; status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); read_buf_avail -= in_buf_size; read_buf_ofs += in_buf_size; if (out_buf_size) { if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) { status = TINFL_STATUS_FAILED; break; } file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) { status = TINFL_STATUS_FAILED; break; } } } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); } } if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) { // Make sure the entire file was decompressed, and check its CRC. if ((out_buf_ofs != file_stat.m_uncomp_size) || (file_crc32 != file_stat.m_crc32)) status = TINFL_STATUS_FAILED; } if (!pZip->m_pState->m_pMem) pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); if (pWrite_buf) pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); return status == TINFL_STATUS_DONE; } mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) { int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); if (file_index < 0) return MZ_FALSE; return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) { (void)ofs; return MZ_FWRITE(pBuf, 1, n, (MZ_FILE*)pOpaque); } mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) { mz_bool status; mz_zip_archive_file_stat file_stat; MZ_FILE *pFile; if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; pFile = MZ_FOPEN(pDst_filename, "wb"); if (!pFile) return MZ_FALSE; status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); if (MZ_FCLOSE(pFile) == EOF) return MZ_FALSE; #ifndef MINIZ_NO_TIME if (status) mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); #endif return status; } #endif // #ifndef MINIZ_NO_STDIO mz_bool mz_zip_reader_end(mz_zip_archive *pZip) { if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) return MZ_FALSE; if (pZip->m_pState) { mz_zip_internal_state *pState = pZip->m_pState; pZip->m_pState = NULL; mz_zip_array_clear(pZip, &pState->m_central_dir); mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); #ifndef MINIZ_NO_STDIO if (pState->m_pFile) { MZ_FCLOSE(pState->m_pFile); pState->m_pFile = NULL; } #endif // #ifndef MINIZ_NO_STDIO pZip->m_pFree(pZip->m_pAlloc_opaque, pState); } pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; return MZ_TRUE; } #ifndef MINIZ_NO_STDIO mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) { int file_index = mz_zip_reader_locate_file(pZip, pArchive_filename, NULL, flags); if (file_index < 0) return MZ_FALSE; return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); } #endif // ------------------- .ZIP archive writing #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS static void mz_write_le16(mz_uint8 *p, mz_uint16 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); } static void mz_write_le32(mz_uint8 *p, mz_uint32 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); p[2] = (mz_uint8)(v >> 16); p[3] = (mz_uint8)(v >> 24); } #define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) #define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) { if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) return MZ_FALSE; if (pZip->m_file_offset_alignment) { // Ensure user specified file offset alignment is a power of 2. if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) return MZ_FALSE; } if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; if (!pZip->m_pFree) pZip->m_pFree = def_free_func; if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; pZip->m_archive_size = existing_size; pZip->m_central_directory_file_ofs = 0; pZip->m_total_files = 0; if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) return MZ_FALSE; memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); return MZ_TRUE; } static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_zip_internal_state *pState = pZip->m_pState; mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); #ifdef _MSC_VER if ((!n) || ((0, sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) #else if ((!n) || ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) #endif return 0; if (new_size > pState->m_mem_capacity) { void *pNew_block; size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); while (new_capacity < new_size) new_capacity *= 2; if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) return 0; pState->m_pMem = pNew_block; pState->m_mem_capacity = new_capacity; } memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); pState->m_mem_size = (size_t)new_size; return n; } mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) { pZip->m_pWrite = mz_zip_heap_write_func; pZip->m_pIO_opaque = pZip; if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) return MZ_FALSE; if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) { if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) { mz_zip_writer_end(pZip); return MZ_FALSE; } pZip->m_pState->m_mem_capacity = initial_allocation_size; } return MZ_TRUE; } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) return 0; return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); } mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) { MZ_FILE *pFile; pZip->m_pWrite = mz_zip_file_write_func; pZip->m_pIO_opaque = pZip; if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) return MZ_FALSE; if (NULL == (pFile = MZ_FOPEN(pFilename, "wb"))) { mz_zip_writer_end(pZip); return MZ_FALSE; } pZip->m_pState->m_pFile = pFile; if (size_to_reserve_at_beginning) { mz_uint64 cur_ofs = 0; char buf[4096]; MZ_CLEAR_OBJ(buf); do { size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) { mz_zip_writer_end(pZip); return MZ_FALSE; } cur_ofs += n; size_to_reserve_at_beginning -= n; } while (size_to_reserve_at_beginning); } return MZ_TRUE; } #endif // #ifndef MINIZ_NO_STDIO mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) { mz_zip_internal_state *pState; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) return MZ_FALSE; // No sense in trying to write to an archive that's already at the support max size if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) return MZ_FALSE; pState = pZip->m_pState; if (pState->m_pFile) { #ifdef MINIZ_NO_STDIO pFilename; return MZ_FALSE; #else // Archive is being read from stdio - try to reopen as writable. if (pZip->m_pIO_opaque != pZip) return MZ_FALSE; if (!pFilename) return MZ_FALSE; pZip->m_pWrite = mz_zip_file_write_func; if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) { // The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. mz_zip_reader_end(pZip); return MZ_FALSE; } #endif // #ifdef MINIZ_NO_STDIO } else if (pState->m_pMem) { // Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. if (pZip->m_pIO_opaque != pZip) return MZ_FALSE; pState->m_mem_capacity = pState->m_mem_size; pZip->m_pWrite = mz_zip_heap_write_func; } // Archive is being read via a user provided read function - make sure the user has specified a write function too. else if (!pZip->m_pWrite) return MZ_FALSE; // Start writing new files at the archive's current central directory location. pZip->m_archive_size = pZip->m_central_directory_file_ofs; pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; pZip->m_central_directory_file_ofs = 0; return MZ_TRUE; } mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) { return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); } typedef struct { mz_zip_archive *m_pZip; mz_uint64 m_cur_archive_file_ofs; mz_uint64 m_comp_size; } mz_zip_writer_add_state; static mz_bool mz_zip_writer_add_put_buf_callback(const void* pBuf, int len, void *pUser) { mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) return MZ_FALSE; pState->m_cur_archive_file_ofs += len; pState->m_comp_size += len; return MZ_TRUE; } static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) { (void)pZip; memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, comp_size); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, uncomp_size); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); return MZ_TRUE; } static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) { (void)pZip; memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, comp_size); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, uncomp_size); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_header_ofs); return MZ_TRUE; } static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) { mz_zip_internal_state *pState = pZip->m_pState; mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; size_t orig_central_dir_size = pState->m_central_dir.m_size; mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; // No zip64 support yet if ((local_header_ofs > 0xFFFFFFFF) || (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + comment_size) > 0xFFFFFFFF)) return MZ_FALSE; if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, extra_size, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) return MZ_FALSE; if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) { // Try to push the central directory array back into its original state. mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return MZ_FALSE; } return MZ_TRUE; } static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) { // Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. if (*pArchive_name == '/') return MZ_FALSE; while (*pArchive_name) { if ((*pArchive_name == '\\') || (*pArchive_name == ':')) return MZ_FALSE; pArchive_name++; } return MZ_TRUE; } static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) { mz_uint32 n; if (!pZip->m_file_offset_alignment) return 0; n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); return (pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1); } static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) { char buf[4096]; memset(buf, 0, MZ_MIN(sizeof(buf), n)); while (n) { mz_uint32 s = MZ_MIN(sizeof(buf), n); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) return MZ_FALSE; cur_file_ofs += s; n -= s; } return MZ_TRUE; } mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) { mz_uint16 method = 0, dos_time = 0, dos_date = 0; mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; size_t archive_name_size; mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; tdefl_compressor *pComp = NULL; mz_bool store_data_uncompressed; mz_zip_internal_state *pState; if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; level = level_and_flags & 0xF; store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (pZip->m_total_files == 0xFFFF) || (level > MZ_UBER_COMPRESSION)) return MZ_FALSE; pState = pZip->m_pState; if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) return MZ_FALSE; // No zip64 support yet if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) return MZ_FALSE; if (!mz_zip_writer_validate_archive_name(pArchive_name)) return MZ_FALSE; #ifndef MINIZ_NO_TIME { time_t cur_time; time(&cur_time); mz_zip_time_to_dos_time(cur_time, &dos_time, &dos_date); } #endif // #ifndef MINIZ_NO_TIME archive_name_size = strlen(pArchive_name); if (archive_name_size > 0xFFFF) return MZ_FALSE; num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); // no zip64 support yet if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF)) return MZ_FALSE; if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) { // Set DOS Subdirectory attribute bit. ext_attributes |= 0x10; // Subdirectories cannot contain data. if ((buf_size) || (uncomp_size)) return MZ_FALSE; } // Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size)) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) return MZ_FALSE; if ((!store_data_uncompressed) && (buf_size)) { if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) return MZ_FALSE; } if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return MZ_FALSE; } local_dir_header_ofs += num_alignment_padding_bytes; if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); MZ_CLEAR_OBJ(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return MZ_FALSE; } cur_archive_file_ofs += archive_name_size; if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8*)pBuf, buf_size); uncomp_size = buf_size; if (uncomp_size <= 3) { level = 0; store_data_uncompressed = MZ_TRUE; } } if (store_data_uncompressed) { if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return MZ_FALSE; } cur_archive_file_ofs += buf_size; comp_size = buf_size; if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) method = MZ_DEFLATED; } else if (buf_size) { mz_zip_writer_add_state state; state.m_pZip = pZip; state.m_cur_archive_file_ofs = cur_archive_file_ofs; state.m_comp_size = 0; if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return MZ_FALSE; } comp_size = state.m_comp_size; cur_archive_file_ofs = state.m_cur_archive_file_ofs; method = MZ_DEFLATED; } pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); pComp = NULL; // no zip64 support yet if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) return MZ_FALSE; if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) return MZ_FALSE; if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return MZ_FALSE; if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes)) return MZ_FALSE; pZip->m_total_files++; pZip->m_archive_size = cur_archive_file_ofs; return MZ_TRUE; } #ifndef MINIZ_NO_STDIO mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) { mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0; size_t archive_name_size; mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; MZ_FILE *pSrc_file = NULL; if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; level = level_and_flags & 0xF; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) return MZ_FALSE; if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) return MZ_FALSE; if (!mz_zip_writer_validate_archive_name(pArchive_name)) return MZ_FALSE; archive_name_size = strlen(pArchive_name); if (archive_name_size > 0xFFFF) return MZ_FALSE; num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); // no zip64 support yet if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF)) return MZ_FALSE; if (!mz_zip_get_file_modified_time(pSrc_filename, &dos_time, &dos_date)) return MZ_FALSE; pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); if (!pSrc_file) return MZ_FALSE; MZ_FSEEK64(pSrc_file, 0, SEEK_END); uncomp_size = MZ_FTELL64(pSrc_file); MZ_FSEEK64(pSrc_file, 0, SEEK_SET); if (uncomp_size > 0xFFFFFFFF) { // No zip64 support yet MZ_FCLOSE(pSrc_file); return MZ_FALSE; } if (uncomp_size <= 3) level = 0; if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) { MZ_FCLOSE(pSrc_file); return MZ_FALSE; } local_dir_header_ofs += num_alignment_padding_bytes; if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); MZ_CLEAR_OBJ(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { MZ_FCLOSE(pSrc_file); return MZ_FALSE; } cur_archive_file_ofs += archive_name_size; if (uncomp_size) { mz_uint64 uncomp_remaining = uncomp_size; void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); if (!pRead_buf) { MZ_FCLOSE(pSrc_file); return MZ_FALSE; } if (!level) { while (uncomp_remaining) { mz_uint n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining); if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); MZ_FCLOSE(pSrc_file); return MZ_FALSE; } uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); uncomp_remaining -= n; cur_archive_file_ofs += n; } comp_size = uncomp_size; } else { mz_bool result = MZ_FALSE; mz_zip_writer_add_state state; tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); if (!pComp) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); MZ_FCLOSE(pSrc_file); return MZ_FALSE; } state.m_pZip = pZip; state.m_cur_archive_file_ofs = cur_archive_file_ofs; state.m_comp_size = 0; if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); MZ_FCLOSE(pSrc_file); return MZ_FALSE; } for ( ; ; ) { size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, MZ_ZIP_MAX_IO_BUF_SIZE); tdefl_status status; if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size) break; uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size); uncomp_remaining -= in_buf_size; status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH); if (status == TDEFL_STATUS_DONE) { result = MZ_TRUE; break; } else if (status != TDEFL_STATUS_OKAY) break; } pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); if (!result) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); MZ_FCLOSE(pSrc_file); return MZ_FALSE; } comp_size = state.m_comp_size; cur_archive_file_ofs = state.m_cur_archive_file_ofs; method = MZ_DEFLATED; } pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); } MZ_FCLOSE(pSrc_file); pSrc_file = NULL; // no zip64 support yet if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) return MZ_FALSE; if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) return MZ_FALSE; if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return MZ_FALSE; if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes)) return MZ_FALSE; pZip->m_total_files++; pZip->m_archive_size = cur_archive_file_ofs; return MZ_TRUE; } #endif // #ifndef MINIZ_NO_STDIO mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index) { mz_uint n, bit_flags, num_alignment_padding_bytes; mz_uint64 comp_bytes_remaining, local_dir_header_ofs; mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; mz_uint8 central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; size_t orig_central_dir_size; mz_zip_internal_state *pState; void *pBuf; const mz_uint8 *pSrc_central_header; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) return MZ_FALSE; if (NULL == (pSrc_central_header = mz_zip_reader_get_cdh(pSource_zip, file_index))) return MZ_FALSE; pState = pZip->m_pState; num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); // no zip64 support yet if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) return MZ_FALSE; cur_src_file_ofs = MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS); cur_dst_file_ofs = pZip->m_archive_size; if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return MZ_FALSE; if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return MZ_FALSE; cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) return MZ_FALSE; cur_dst_file_ofs += num_alignment_padding_bytes; local_dir_header_ofs = cur_dst_file_ofs; if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return MZ_FALSE; cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; n = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); comp_bytes_remaining = n + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(sizeof(mz_uint32) * 4, MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining))))) return MZ_FALSE; while (comp_bytes_remaining) { n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining); if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return MZ_FALSE; } cur_src_file_ofs += n; if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return MZ_FALSE; } cur_dst_file_ofs += n; comp_bytes_remaining -= n; } bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); if (bit_flags & 8) { // Copy data descriptor if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return MZ_FALSE; } n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == 0x08074b50) ? 4 : 3); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return MZ_FALSE; } cur_src_file_ofs += n; cur_dst_file_ofs += n; } pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); // no zip64 support yet if (cur_dst_file_ofs > 0xFFFFFFFF) return MZ_FALSE; orig_central_dir_size = pState->m_central_dir.m_size; memcpy(central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); MZ_WRITE_LE32(central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) return MZ_FALSE; n = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n)) { mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return MZ_FALSE; } if (pState->m_central_dir.m_size > 0xFFFFFFFF) return MZ_FALSE; n = (mz_uint32)orig_central_dir_size; if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) { mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return MZ_FALSE; } pZip->m_total_files++; pZip->m_archive_size = cur_dst_file_ofs; return MZ_TRUE; } mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) { mz_zip_internal_state *pState; mz_uint64 central_dir_ofs, central_dir_size; mz_uint8 hdr[MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE]; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) return MZ_FALSE; pState = pZip->m_pState; // no zip64 support yet if ((pZip->m_total_files > 0xFFFF) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) return MZ_FALSE; central_dir_ofs = 0; central_dir_size = 0; if (pZip->m_total_files) { // Write central directory central_dir_ofs = pZip->m_archive_size; central_dir_size = pState->m_central_dir.m_size; pZip->m_central_directory_file_ofs = central_dir_ofs; if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) return MZ_FALSE; pZip->m_archive_size += central_dir_size; } // Write end of central directory record MZ_CLEAR_OBJ(hdr); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, central_dir_size); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, central_dir_ofs); if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, sizeof(hdr)) != sizeof(hdr)) return MZ_FALSE; #ifndef MINIZ_NO_STDIO if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) return MZ_FALSE; #endif // #ifndef MINIZ_NO_STDIO pZip->m_archive_size += sizeof(hdr); pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; return MZ_TRUE; } mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize) { if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pSize)) return MZ_FALSE; if (pZip->m_pWrite != mz_zip_heap_write_func) return MZ_FALSE; if (!mz_zip_writer_finalize_archive(pZip)) return MZ_FALSE; *pBuf = pZip->m_pState->m_pMem; *pSize = pZip->m_pState->m_mem_size; pZip->m_pState->m_pMem = NULL; pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; return MZ_TRUE; } mz_bool mz_zip_writer_end(mz_zip_archive *pZip) { mz_zip_internal_state *pState; mz_bool status = MZ_TRUE; if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) return MZ_FALSE; pState = pZip->m_pState; pZip->m_pState = NULL; mz_zip_array_clear(pZip, &pState->m_central_dir); mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); #ifndef MINIZ_NO_STDIO if (pState->m_pFile) { MZ_FCLOSE(pState->m_pFile); pState->m_pFile = NULL; } #endif // #ifndef MINIZ_NO_STDIO if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); pState->m_pMem = NULL; } pZip->m_pFree(pZip->m_pAlloc_opaque, pState); pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; return status; } #ifndef MINIZ_NO_STDIO mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) { mz_bool status, created_new_archive = MZ_FALSE; mz_zip_archive zip_archive; struct MZ_FILE_STAT_STRUCT file_stat; MZ_CLEAR_OBJ(zip_archive); if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) return MZ_FALSE; if (!mz_zip_writer_validate_archive_name(pArchive_name)) return MZ_FALSE; if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) { // Create a new archive. if (!mz_zip_writer_init_file(&zip_archive, pZip_filename, 0)) return MZ_FALSE; created_new_archive = MZ_TRUE; } else { // Append to an existing archive. if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) return MZ_FALSE; if (!mz_zip_writer_init_from_reader(&zip_archive, pZip_filename)) { mz_zip_reader_end(&zip_archive); return MZ_FALSE; } } status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); // Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) if (!mz_zip_writer_finalize_archive(&zip_archive)) status = MZ_FALSE; if (!mz_zip_writer_end(&zip_archive)) status = MZ_FALSE; if ((!status) && (created_new_archive)) { // It's a new archive and something went wrong, so just delete it. int ignoredStatus = MZ_DELETE_FILE(pZip_filename); (void)ignoredStatus; } return status; } void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) { int file_index; mz_zip_archive zip_archive; void *p = NULL; if (pSize) *pSize = 0; if ((!pZip_filename) || (!pArchive_name)) return NULL; MZ_CLEAR_OBJ(zip_archive); if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) return NULL; if ((file_index = mz_zip_reader_locate_file(&zip_archive, pArchive_name, NULL, flags)) >= 0) p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); mz_zip_reader_end(&zip_archive); return p; } #endif // #ifndef MINIZ_NO_STDIO #endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS #endif // #ifndef MINIZ_NO_ARCHIVE_APIS #ifdef __cplusplus } #endif #endif // MINIZ_HEADER_FILE_ONLY /* This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. 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 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. For more information, please refer to */ ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TMXLite/miniz.h ================================================ /* miniz public domain replacement for zlib. See miniz.c for more information. */ #ifndef MINIZ_HEADER_FILE_ONLY #define MINIZ_HEADER_FILE_ONLY #include "miniz.c" #endif //MINIZ_HEADER_FILE_ONLY ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TinyFileDialogs/TinyDialogBox.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2021 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef TINYDIALOGBOX_H_INCLUDED #define TINYDIALOGBOX_H_INCLUDED #if !defined(__ANDROID__) #include "../../ext_lib/TinyFileDialogs/tinyfiledialogs.h" #include "../../system/function/GameFunction.h" //////////////////////////////////////////////////////////// /// tinyString is a custom type it changes depending on the target platform (windows / linux) /// on windows it becomes @a wchar_t @a const* and on linux @a char @a const* //////////////////////////////////////////////////////////// #if !defined(SFML_SYSTEM_LINUX) && !defined(IS_ENGINE_LINUX) typedef wchar_t const* tinyString; #else typedef char const* tinyString; #endif namespace is { /// return file path static tinyString TINY_FILE_DIALOGBOX_PATH; /// \brief allow use tinyfiledialog lib to show windows diaglog box class TinyDialogBox { public: enum FileDialogType { SAVE_FILE, LOAD_FILE }; enum DialogType { OK, OKCANCEL, YESNO }; enum IconType { INFO, WARNING, ERROR_ICO, QUESTION }; static tinyString const enumDialogTypeToStr(DialogType val) { switch (val) { case DialogType::OKCANCEL : return ( #if !defined(SFML_SYSTEM_LINUX) && !defined(IS_ENGINE_LINUX) L"okcancel" #else "okcancel" #endif ); break; case DialogType::YESNO : return ( #if !defined(SFML_SYSTEM_LINUX) && !defined(IS_ENGINE_LINUX) L"yesno" #else "yesno" #endif ); break; default : return ( #if !defined(SFML_SYSTEM_LINUX) && !defined(IS_ENGINE_LINUX) L"ok" #else "ok" #endif ); break; } } static tinyString const enumIconTypeToStr(IconType val) { switch (val) { case IconType::QUESTION : return ( #if !defined(SFML_SYSTEM_LINUX) && !defined(IS_ENGINE_LINUX) L"question" #else "question" #endif ); break; case IconType::ERROR_ICO : return ( #if !defined(SFML_SYSTEM_LINUX) && !defined(IS_ENGINE_LINUX) L"error" #else "error" #endif ); break; case IconType::WARNING : return ( #if !defined(SFML_SYSTEM_LINUX) && !defined(IS_ENGINE_LINUX) L"warning" #else "warning" #endif ); break; default : return ( #if !defined(SFML_SYSTEM_LINUX) && !defined(IS_ENGINE_LINUX) L"info" #else "info" #endif ); break; } } //////////////////////////////////////////////////////////// /// \brief show windows dialog /// /// \param saveFile true to display a file save dialog box, false to display a load file dialog box /// /// \return @a 1 when user click on @a OK button and @a 0 when @a CANCEL or @a NO button is clicked /// //////////////////////////////////////////////////////////// static int showDialogBox(const std::string& title, const std::string& msg, DialogType dialogType, IconType iconType ) { tinyString const _dialogType = enumDialogTypeToStr(dialogType); tinyString const _iconType = enumIconTypeToStr(iconType); return ( #if !defined(SFML_SYSTEM_LINUX) && !defined(IS_ENGINE_LINUX) tinyfd_messageBoxW(is::strToWStr(title).c_str(), is::strToWStr(msg).c_str(), _dialogType, _iconType, 1) #else tinyfd_messageBox(title.c_str(), msg.c_str(), _dialogType, _iconType, 1) #endif ); } //////////////////////////////////////////////////////////// /// \brief show windows file dialog /// /// \param saveFile @a true to display a file save dialog box, @a false to display a load file dialog box /// /// \return file path if the function succeeded and @a "" (empty string) is faliled /// //////////////////////////////////////////////////////////// static std::string showFileDialogBox(FileDialogType type, const std::string& title, tinyString filterPatterns[], const std::string& fileName = "file", const std::string& msgError = "Unable to access file!", const std::string& errTitle = "Error" ) { if (type == FileDialogType::SAVE_FILE) { TINY_FILE_DIALOGBOX_PATH = #if !defined(SFML_SYSTEM_LINUX) && !defined(IS_ENGINE_LINUX) tinyfd_saveFileDialogW(is::strToWStr(title).c_str(), is::strToWStr(fileName).c_str(), 2, filterPatterns, NULL); #else tinyfd_saveFileDialog(title.c_str(), fileName.c_str(), 2, filterPatterns, NULL); #endif } else { TINY_FILE_DIALOGBOX_PATH = #if !defined(SFML_SYSTEM_LINUX) && !defined(IS_ENGINE_LINUX) tinyfd_openFileDialogW(is::strToWStr(title).c_str(), L"", 2, filterPatterns, NULL, 0); #else tinyfd_openFileDialog(title.c_str(), "", 2, filterPatterns, NULL, 0); #endif } if (!TINY_FILE_DIALOGBOX_PATH) { showDialogBox(errTitle, msgError, DialogType::OK, IconType::ERROR_ICO); } else { return ( #if !defined(SFML_SYSTEM_LINUX) && !defined(IS_ENGINE_LINUX) is::w_chart_tToStr(TINY_FILE_DIALOGBOX_PATH) #else TINY_FILE_DIALOGBOX_PATH #endif ); } return ""; } //////////////////////////////////////////////////////////// /// \brief show windows select folder dialog /// /// \return directory path if the function succeeded and @a "" (empty string) is faliled /// //////////////////////////////////////////////////////////// static std::string showFolderDialogBox(const std::string& title, const std::string& defaultPath #if !defined(SFML_SYSTEM_LINUX) && !defined(IS_ENGINE_LINUX) = "C:\\", #else = "/usr/local", #endif const std::string& msgError = "Unable to access folder!", const std::string& errTitle = "Error" ) { TINY_FILE_DIALOGBOX_PATH = #if !defined(SFML_SYSTEM_LINUX) && !defined(IS_ENGINE_LINUX) tinyfd_selectFolderDialogW(is::strToWStr(title).c_str(), is::strToWStr(defaultPath).c_str()); #else tinyfd_selectFolderDialog(title.c_str(), defaultPath.c_str()); #endif if (!TINY_FILE_DIALOGBOX_PATH) { showDialogBox(errTitle, msgError, DialogType::OK, IconType::ERROR_ICO); } else { return ( #if !defined(SFML_SYSTEM_LINUX) && !defined(IS_ENGINE_LINUX) is::w_chart_tToStr(TINY_FILE_DIALOGBOX_PATH) #else TINY_FILE_DIALOGBOX_PATH #endif ); } return ""; } }; } #endif // defined #endif // TINYDIALOGBOX_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TinyFileDialogs/tinyfiledialogs.cpp ================================================ /*_________ / \ tinyfiledialogs.c v3.4.3 [Dec 8, 2019] zlib licence |tiny file| Unique code file created [November 9, 2014] | dialogs | Copyright (c) 2014 - 2018 Guillaume Vareille http://ysengrin.com \____ ___/ http://tinyfiledialogs.sourceforge.net \| git clone http://git.code.sf.net/p/tinyfiledialogs/code tinyfd ____________________________________________ | | | email: tinyfiledialogs at ysengrin.com | |____________________________________________| ___________________________________________________________________ | | | the windows only wchar_t UTF-16 prototypes are in the header file | |___________________________________________________________________| Please upvote my stackoverflow answer https://stackoverflow.com/a/47651444 tiny file dialogs (cross-platform C C++) InputBox PasswordBox MessageBox ColorPicker OpenFileDialog SaveFileDialog SelectFolderDialog Native dialog library for WINDOWS MAC OSX GTK+ QT CONSOLE & more SSH supported via automatic switch to console mode or X11 forwarding one C file + a header (add them to your C or C++ project) with 8 functions: - beep - notify popup (tray) - message & question - input & password - save file - open file(s) - select folder - color picker Complements OpenGL Vulkan GLFW GLUT GLUI VTK SFML TGUI SDL Ogre Unity3d ION OpenCV CEGUI MathGL GLM CPW GLOW Open3D IMGUI MyGUI GLT NGL STB & GUI less programs NO INIT NO MAIN LOOP NO LINKING NO INCLUDE The dialogs can be forced into console mode Windows (XP to 10) ASCII MBCS UTF-8 UTF-16 - native code & vbs create the graphic dialogs - enhanced console mode can use dialog.exe from http://andrear.altervista.org/home/cdialog.php - basic console input Unix (command line calls) ASCII UTF-8 - applescript, kdialog, zenity - python (2 or 3) + tkinter + python-dbus (optional) - dialog (opens a console if needed) - basic console input The same executable can run across desktops & distributions C89 & C++98 compliant: tested with C & C++ compilers VisualStudio MinGW-gcc GCC Clang TinyCC OpenWatcom-v2 BorlandC SunCC ZapCC on Windows Mac Linux Bsd Solaris Minix Raspbian using Gnome Kde Enlightenment Mate Cinnamon Budgie Unity Lxde Lxqt Xfce WindowMaker IceWm Cde Jds OpenBox Awesome Jwm Xdm Bindings for LUA and C# dll, Haskell Included in LWJGL(java), Rust, Allegrobasic Thanks for contributions, bug corrections & thorough testing to: - Don Heyse http://ldglite.sf.net for bug corrections & thorough testing! - Paul Rouget - License - 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. */ // allows to deactivate the execution of certain codes because it does not support c++11 #define AVOID_EXECUTION #ifndef __sun #define _POSIX_C_SOURCE 2 /* to accept POSIX 2 in old ANSI C standards */ #endif #include #include #include #include #include #include #include "tinyfiledialogs.h" /* #define TINYFD_NOLIB */ #ifdef _WIN32 #ifdef __BORLANDC__ #define _getch getch #endif #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0500 #endif #ifndef TINYFD_NOLIB #include /*#define TINYFD_NOSELECTFOLDERWIN*/ #ifndef TINYFD_NOSELECTFOLDERWIN #include #endif /*TINYFD_NOSELECTFOLDERWIN*/ #endif #include #include #define TINYFD_NOCCSUNICODE #define SLASH "\\" int tinyfd_winUtf8 = 0 ; /* on windows string char can be 0:MBCS or 1:UTF-8 */ #else #include #include #include /* on old systems try instead */ #include #include #include /* on old systems try instead */ #define SLASH "/" #endif /* _WIN32 */ #define MAX_PATH_OR_CMD 1024 /* _MAX_PATH or MAX_PATH */ #define MAX_MULTIPLE_FILES 32 char const tinyfd_version [8] = "3.4.3"; int tinyfd_verbose = 0 ; /* on unix: prints the command line calls */ int tinyfd_silent = 1 ; /* 1 (default) or 0 : on unix, hide errors and warnings from called dialog*/ #if defined(TINYFD_NOLIB) && defined(_WIN32) int tinyfd_forceConsole = 1 ; #else int tinyfd_forceConsole = 0 ; /* 0 (default) or 1 */ #endif /* for unix & windows: 0 (graphic mode) or 1 (console mode). 0: try to use a graphic solution, if it fails then it uses console mode. 1: forces all dialogs into console mode even when the X server is present, if the package dialog (and a console is present) or dialog.exe is installed. on windows it only make sense for console applications */ char tinyfd_response[1024]; /* if you pass "tinyfd_query" as aTitle, the functions will not display the dialogs but and return 0 for console mode, 1 for graphic mode. tinyfd_response is then filled with the retain solution. possible values for tinyfd_response are (all lowercase) for graphic mode: windows_wchar windows applescript kdialog zenity zenity3 matedialog qarma python2-tkinter python3-tkinter python-dbus perl-dbus gxmessage gmessage xmessage xdialog gdialog for console mode: dialog whiptail basicinput no_solution */ #if defined(TINYFD_NOLIB) && defined(_WIN32) static int gWarningDisplayed = 1 ; #else static int gWarningDisplayed = 0 ; #endif static char const gTitle[]="missing software! (we will try basic console input)"; #ifdef _WIN32 char const tinyfd_needs[] = "\ ___________\n\ / \\ \n\ | tiny file |\n\ | dialogs |\n\ \\_____ ____/\n\ \\|\ \ntiny file dialogs on Windows needs:\ \n a graphic display\ \nor dialog.exe (enhanced console mode)\ \nor a console for basic input"; #else char const tinyfd_needs[] = "\ ___________\n\ / \\ \n\ | tiny file |\n\ | dialogs |\n\ \\_____ ____/\n\ \\|\ \ntiny file dialogs on UNIX needs:\ \n applescript\ \nor kdialog\ \nor zenity (or matedialog or qarma)\ \nor python (2 or 3)\ \n + tkinter + python-dbus (optional)\ \nor dialog (opens console if needed)\ \nor xterm + bash\ \n (opens console for basic input)\ \nor existing console for basic input"; #endif #ifdef _MSC_VER #pragma warning(disable:4996) /* allows usage of strncpy, strcpy, strcat, sprintf, fopen */ #pragma warning(disable:4100) /* allows usage of strncpy, strcpy, strcat, sprintf, fopen */ #pragma warning(disable:4706) /* allows usage of strncpy, strcpy, strcat, sprintf, fopen */ #endif static char * getPathWithoutFinalSlash( char * const aoDestination, /* make sure it is allocated, use _MAX_PATH */ char const * const aSource) /* aoDestination and aSource can be the same */ { char const * lTmp ; if ( aSource ) { lTmp = strrchr(aSource, '/'); if (!lTmp) { lTmp = strrchr(aSource, '\\'); } if (lTmp) { strncpy(aoDestination, aSource, lTmp - aSource ); aoDestination[lTmp - aSource] = '\0'; } else { * aoDestination = '\0'; } } else { * aoDestination = '\0'; } return aoDestination; } static char * getLastName( char * const aoDestination, /* make sure it is allocated */ char const * const aSource) { /* copy the last name after '/' or '\' */ char const * lTmp ; if ( aSource ) { lTmp = strrchr(aSource, '/'); if (!lTmp) { lTmp = strrchr(aSource, '\\'); } if (lTmp) { strcpy(aoDestination, lTmp + 1); } else { strcpy(aoDestination, aSource); } } else { * aoDestination = '\0'; } return aoDestination; } static void ensureFinalSlash( char * const aioString ) { if ( aioString && strlen( aioString ) ) { char * lastcar = aioString + strlen( aioString ) - 1 ; if ( strncmp( lastcar , SLASH , 1 ) ) { strcat( lastcar , SLASH ) ; } } } static void Hex2RGB( char const aHexRGB [8] , unsigned char aoResultRGB [3] ) { char lColorChannel [8] ; if ( aoResultRGB ) { if ( aHexRGB ) { strcpy(lColorChannel, aHexRGB ) ; aoResultRGB[2] = (unsigned char)strtoul(lColorChannel+5,NULL,16); lColorChannel[5] = '\0'; aoResultRGB[1] = (unsigned char)strtoul(lColorChannel+3,NULL,16); lColorChannel[3] = '\0'; aoResultRGB[0] = (unsigned char)strtoul(lColorChannel+1,NULL,16); /* printf("%d %d %d\n", aoResultRGB[0], aoResultRGB[1], aoResultRGB[2]); */ } else { aoResultRGB[0]=0; aoResultRGB[1]=0; aoResultRGB[2]=0; } } } static void RGB2Hex( unsigned char const aRGB [3] , char aoResultHexRGB [8] ) { if ( aoResultHexRGB ) { if ( aRGB ) { #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L sprintf(aoResultHexRGB, "#%02hhx%02hhx%02hhx", aRGB[0], aRGB[1], aRGB[2]); #else sprintf(aoResultHexRGB, "#%02hx%02hx%02hx", aRGB[0], aRGB[1], aRGB[2]); #endif /*printf("aoResultHexRGB %s\n", aoResultHexRGB);*/ } else { aoResultHexRGB[0]=0; aoResultHexRGB[1]=0; aoResultHexRGB[2]=0; } } } static void replaceSubStr( char const * const aSource , char const * const aOldSubStr , char const * const aNewSubStr , char * const aoDestination ) { char const * pOccurence ; char const * p ; char const * lNewSubStr = "" ; size_t lOldSubLen = strlen( aOldSubStr ) ; if ( ! aSource ) { * aoDestination = '\0' ; return ; } if ( ! aOldSubStr ) { strcpy( aoDestination , aSource ) ; return ; } if ( aNewSubStr ) { lNewSubStr = aNewSubStr ; } p = aSource ; * aoDestination = '\0' ; while ( ( pOccurence = strstr( p , aOldSubStr ) ) != NULL ) { strncat( aoDestination , p , pOccurence - p ) ; strcat( aoDestination , lNewSubStr ) ; p = pOccurence + lOldSubLen ; } strcat( aoDestination , p ) ; } static int filenameValid( char const * const aFileNameWithoutPath ) { if ( ! aFileNameWithoutPath || ! strlen(aFileNameWithoutPath) || strpbrk(aFileNameWithoutPath , "\\/:*?\"<>|") ) { return 0 ; } return 1 ; } #ifndef _WIN32 static int fileExists( char const * const aFilePathAndName ) { FILE * lIn ; if ( ! aFilePathAndName || ! strlen(aFilePathAndName) ) { return 0 ; } lIn = fopen( aFilePathAndName , "r" ) ; if ( ! lIn ) { return 0 ; } fclose( lIn ) ; return 1 ; } #elif defined(TINYFD_NOLIB) static int fileExists( char const * const aFilePathAndName ) { FILE * lIn ; if ( ! aFilePathAndName || ! strlen(aFilePathAndName) ) { return 0 ; } if ( tinyfd_winUtf8 ) return 1; /* we cannot test */ lIn = fopen( aFilePathAndName , "r" ) ; if ( ! lIn ) { return 0 ; } fclose( lIn ) ; return 1 ; } #endif static void wipefile(char const * const aFilename) { int i; struct stat st; FILE * lIn; if (stat(aFilename, &st) == 0) { if ((lIn = fopen(aFilename, "w"))) { for (i = 0; i < st.st_size; i++) { fputc('A', lIn); } } fclose(lIn); } } #ifdef _WIN32 static int replaceChr( char * const aString , char const aOldChr , char const aNewChr ) { char * p ; int lRes = 0 ; if ( ! aString ) { return 0 ; } if ( aOldChr == aNewChr ) { return 0 ; } p = aString ; while ( (p = strchr( p , aOldChr )) ) { * p = aNewChr ; p ++ ; lRes = 1 ; } return lRes ; } #ifdef TINYFD_NOLIB static int dirExists(char const * const aDirPath) { struct stat lInfo; if (!aDirPath || !strlen(aDirPath)) return 0; if (stat(aDirPath, &lInfo) != 0) return 0; else if ( tinyfd_winUtf8 ) return 1; /* we cannot test */ else if (lInfo.st_mode & S_IFDIR) return 1; else return 0; } void tinyfd_beep(void) { printf("\a"); } #else /* ndef TINYFD_NOLIB */ void tinyfd_beep(void) { Beep(440,300); } static void wipefileW(wchar_t const * const aFilename) { int i; struct _stat st; FILE * lIn; if (_wstat(aFilename, &st) == 0) { if ((lIn = _wfopen(aFilename, L"w"))) { for (i = 0; i < st.st_size; i++) { fputc('A', lIn); } } fclose(lIn); } } static wchar_t * getPathWithoutFinalSlashW( wchar_t * const aoDestination, /* make sure it is allocated, use _MAX_PATH */ wchar_t const * const aSource) /* aoDestination and aSource can be the same */ { wchar_t const * lTmp; if (aSource) { lTmp = wcsrchr(aSource, L'/'); if (!lTmp) { lTmp = wcsrchr(aSource, L'\\'); } if (lTmp) { wcsncpy(aoDestination, aSource, lTmp - aSource); aoDestination[lTmp - aSource] = L'\0'; } else { *aoDestination = L'\0'; } } else { *aoDestination = L'\0'; } return aoDestination; } static wchar_t * getLastNameW( wchar_t * const aoDestination, /* make sure it is allocated */ wchar_t const * const aSource) { /* copy the last name after '/' or '\' */ wchar_t const * lTmp; if (aSource) { lTmp = wcsrchr(aSource, L'/'); if (!lTmp) { lTmp = wcsrchr(aSource, L'\\'); } if (lTmp) { wcscpy(aoDestination, lTmp + 1); } else { wcscpy(aoDestination, aSource); } } else { *aoDestination = L'\0'; } return aoDestination; } static void Hex2RGBW(wchar_t const aHexRGB[8], unsigned char aoResultRGB[3]) { wchar_t lColorChannel[8]; if (aoResultRGB) { if (aHexRGB) { wcscpy(lColorChannel, aHexRGB); aoResultRGB[2] = (unsigned char)wcstoul(lColorChannel + 5, NULL, 16); lColorChannel[5] = '\0'; aoResultRGB[1] = (unsigned char)wcstoul(lColorChannel + 3, NULL, 16); lColorChannel[3] = '\0'; aoResultRGB[0] = (unsigned char)wcstoul(lColorChannel + 1, NULL, 16); /* printf("%d %d %d\n", aoResultRGB[0], aoResultRGB[1], aoResultRGB[2]); */ } else { aoResultRGB[0] = 0; aoResultRGB[1] = 0; aoResultRGB[2] = 0; } } } static void RGB2HexW( unsigned char const aRGB[3], wchar_t aoResultHexRGB[8]) { if (aoResultHexRGB) { if (aRGB) { /* wprintf(L"aoResultHexRGB %s\n", aoResultHexRGB); */ #if !defined(AVOID_EXECUTION) swprintf(aoResultHexRGB, #if !defined(__BORLANDC__) && !defined(__TINYC__) && ( !defined(__GNUC__) || (__GNUC__) >= 5 ) 8, #endif #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L L"#%02hhx%02hhx%02hhx", aRGB[0], aRGB[1], aRGB[2]); #else L"#%02hx%02hx%02hx", aRGB[0], aRGB[1], aRGB[2]); #endif #endif } else { aoResultHexRGB[0] = 0; aoResultHexRGB[1] = 0; aoResultHexRGB[2] = 0; } } } #if !defined(WC_ERR_INVALID_CHARS) /* undefined prior to Vista, so not yet in MINGW header file */ #define WC_ERR_INVALID_CHARS 0x00000080 #endif static int sizeUtf16(char const * const aUtf8string) { return MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, aUtf8string, -1, NULL, 0); } static int sizeUtf8(wchar_t const * const aUtf16string) { return WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, aUtf16string, -1, NULL, 0, NULL, NULL); } static int sizeMbcs(wchar_t const * const aMbcsString) { int lRes = WideCharToMultiByte(CP_ACP, 0, aMbcsString, -1, NULL, 0, NULL, NULL); /* DWORD licic = GetLastError(); */ return lRes; } static wchar_t * utf8to16(char const * const aUtf8string) { wchar_t * lUtf16string ; int lSize = sizeUtf16(aUtf8string); lUtf16string = (wchar_t *) malloc( lSize * sizeof(wchar_t) ); lSize = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, aUtf8string, -1, lUtf16string, lSize); if (lSize == 0) { free(lUtf16string); return NULL; } return lUtf16string; } static wchar_t * mbcsTo16(char const * const aMbcsString) { wchar_t * lMbcsString; int lSize = sizeUtf16(aMbcsString); lMbcsString = (wchar_t *)malloc(lSize * sizeof(wchar_t)); lSize = MultiByteToWideChar(CP_ACP, 0, aMbcsString, -1, lMbcsString, lSize); if (lSize == 0) { free(lMbcsString); return NULL; } return lMbcsString; } static char * utf16to8(wchar_t const * const aUtf16string) { char * lUtf8string ; int lSize = sizeUtf8(aUtf16string); lUtf8string = (char *) malloc( lSize ); lSize = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, aUtf16string, -1, lUtf8string, lSize, NULL, NULL); if (lSize == 0) { free(lUtf8string); return NULL; } return lUtf8string; } static char * utf16toMbcs(wchar_t const * const aUtf16string) { char * lMbcsString; int lSize = sizeMbcs(aUtf16string); lMbcsString = (char *)malloc(lSize); lSize = WideCharToMultiByte(CP_ACP, 0, aUtf16string, -1, lMbcsString, lSize, NULL, NULL); if (lSize == 0) { free(lMbcsString); return NULL; } return lMbcsString; } static int dirExists(char const * const aDirPath) { struct _stat lInfo; wchar_t * lTmpWChar; int lStatRet; int lDirLen; if (!aDirPath) return 0; lDirLen = strlen(aDirPath); if (!lDirLen) return 1; if ( (lDirLen == 2) && (aDirPath[1] == ':') ) return 1; if (tinyfd_winUtf8) { lTmpWChar = utf8to16(aDirPath); lStatRet = _wstat(lTmpWChar, &lInfo); free(lTmpWChar); if (lStatRet != 0) return 0; else if (lInfo.st_mode & S_IFDIR) return 1; else return 0; } else if (_stat(aDirPath, &lInfo) != 0) return 0; else if (lInfo.st_mode & S_IFDIR) return 1; else return 0; } static int fileExists(char const * const aFilePathAndName) { struct _stat lInfo; wchar_t * lTmpWChar; int lStatRet; FILE * lIn; if (!aFilePathAndName || !strlen(aFilePathAndName)) { return 0; } if (tinyfd_winUtf8) { lTmpWChar = utf8to16(aFilePathAndName); lStatRet = _wstat(lTmpWChar, &lInfo); free(lTmpWChar); if (lStatRet != 0) return 0; else if (lInfo.st_mode & _S_IFREG) return 1; else return 0; } else { lIn = fopen(aFilePathAndName, "r"); if (!lIn) { return 0; } fclose(lIn); return 1; } } static int replaceWchar(wchar_t * const aString, wchar_t const aOldChr, wchar_t const aNewChr) { wchar_t * p; int lRes = 0; if (!aString) { return 0; } if (aOldChr == aNewChr) { return 0; } p = aString; while ((p = wcsrchr(p, aOldChr))) { *p = aNewChr; #ifdef TINYFD_NOCCSUNICODE p++; #endif p++; lRes = 1; } return lRes; } #endif /* TINYFD_NOLIB */ #endif /* _WIN32 */ /* source and destination can be the same or ovelap*/ static char const * ensureFilesExist(char * const aDestination, char const * const aSourcePathsAndNames) { char * lDestination = aDestination; char const * p; char const * p2; size_t lLen; if (!aSourcePathsAndNames) { return NULL; } lLen = strlen(aSourcePathsAndNames); if (!lLen) { return NULL; } p = aSourcePathsAndNames; while ((p2 = strchr(p, '|')) != NULL) { lLen = p2 - p; memmove(lDestination, p, lLen); lDestination[lLen] = '\0'; if (fileExists(lDestination)) { lDestination += lLen; *lDestination = '|'; lDestination++; } p = p2 + 1; } if (fileExists(p)) { lLen = strlen(p); memmove(lDestination, p, lLen); lDestination[lLen] = '\0'; } else { *(lDestination - 1) = '\0'; } return aDestination; } #ifdef _WIN32 #ifndef TINYFD_NOLIB static int __stdcall EnumThreadWndProc(HWND hwnd, LPARAM lParam) { wchar_t lTitleName[MAX_PATH]; GetWindowTextW(hwnd, lTitleName, MAX_PATH); /* wprintf(L"lTitleName %ls \n", lTitleName); */ if (wcscmp(L"tinyfiledialogsTopWindow", lTitleName) == 0) { SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); return 0; } return 1; } static void hiddenConsoleW(wchar_t const * const aString, wchar_t const * const aDialogTitle, int const aInFront) { STARTUPINFOW StartupInfo; PROCESS_INFORMATION ProcessInfo; if (!aString || !wcslen(aString) ) return; memset(&StartupInfo, 0, sizeof(StartupInfo)); StartupInfo.cb = sizeof(STARTUPINFOW); StartupInfo.dwFlags = STARTF_USESHOWWINDOW; StartupInfo.wShowWindow = SW_HIDE; if (!CreateProcessW(NULL, (LPWSTR)aString, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &StartupInfo, &ProcessInfo)) { return; /* GetLastError(); */ } WaitForInputIdle(ProcessInfo.hProcess, INFINITE); if (aInFront) { while (EnumWindows(EnumThreadWndProc, (LPARAM)NULL)) {} SetWindowTextW(GetForegroundWindow(), aDialogTitle); } WaitForSingleObject(ProcessInfo.hProcess, INFINITE); CloseHandle(ProcessInfo.hThread); CloseHandle(ProcessInfo.hProcess); } int tinyfd_messageBoxW( wchar_t const * const aTitle, /* NULL or "" */ wchar_t const * const aMessage, /* NULL or "" may contain \n and \t */ wchar_t const * const aDialogType, /* "ok" "okcancel" "yesno" "yesnocancel" */ wchar_t const * const aIconType, /* "info" "warning" "error" "question" */ int const aDefaultButton) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */ { int lBoxReturnValue; UINT aCode; if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return 1; } if (aIconType && !wcscmp(L"warning", aIconType)) { aCode = MB_ICONWARNING; } else if (aIconType && !wcscmp(L"error", aIconType)) { aCode = MB_ICONERROR; } else if (aIconType && !wcscmp(L"question", aIconType)) { aCode = MB_ICONQUESTION; } else { aCode = MB_ICONINFORMATION; } if (aDialogType && !wcscmp(L"okcancel", aDialogType)) { aCode += MB_OKCANCEL; if (!aDefaultButton) { aCode += MB_DEFBUTTON2; } } else if (aDialogType && !wcscmp(L"yesno", aDialogType)) { aCode += MB_YESNO; if (!aDefaultButton) { aCode += MB_DEFBUTTON2; } } else { aCode += MB_OK; } aCode += MB_TOPMOST; lBoxReturnValue = MessageBoxW(GetForegroundWindow(), aMessage, aTitle, aCode); if (((aDialogType && wcscmp(L"okcancel", aDialogType) && wcscmp(L"yesno", aDialogType))) || (lBoxReturnValue == IDOK) || (lBoxReturnValue == IDYES)) { return 1; } else { return 0; } } static int messageBoxWinGui8( char const * const aTitle, /* NULL or "" */ char const * const aMessage, /* NULL or "" may contain \n and \t */ char const * const aDialogType, /* "ok" "okcancel" "yesno" "yesnocancel" */ char const * const aIconType, /* "info" "warning" "error" "question" */ int const aDefaultButton) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */ { int lIntRetVal; wchar_t * lTitle; wchar_t * lMessage; wchar_t * lDialogType; wchar_t * lIconType; lTitle = utf8to16(aTitle); lMessage = utf8to16(aMessage); lDialogType = utf8to16(aDialogType); lIconType = utf8to16(aIconType); lIntRetVal = tinyfd_messageBoxW(lTitle, lMessage, lDialogType, lIconType, aDefaultButton ); free(lTitle); free(lMessage); free(lDialogType); free(lIconType); return lIntRetVal ; } /* return has only meaning for tinyfd_query */ int tinyfd_notifyPopupW( wchar_t const * const aTitle, /* NULL or L"" */ wchar_t const * const aMessage, /* NULL or L"" may contain \n \t */ wchar_t const * const aIconType) /* L"info" L"warning" L"error" */ { wchar_t * lDialogString; size_t lTitleLen; size_t lMessageLen; size_t lDialogStringLen; if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return 1; } lTitleLen = aTitle ? wcslen(aTitle) : 0; lMessageLen = aMessage ? wcslen(aMessage) : 0; lDialogStringLen = 3 * MAX_PATH_OR_CMD + lTitleLen + lMessageLen; lDialogString = (wchar_t *)malloc(2 * lDialogStringLen); wcscpy(lDialogString, L"powershell.exe -command \"\ function Show-BalloonTip {\ [cmdletbinding()] \ param( \ [string]$Title = ' ', \ [string]$Message = ' ', \ [ValidateSet('info', 'warning', 'error')] \ [string]$IconType = 'info');\ [system.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | Out-Null ; \ $balloon = New-Object System.Windows.Forms.NotifyIcon ; \ $path = Get-Process -id $pid | Select-Object -ExpandProperty Path ; \ $icon = [System.Drawing.Icon]::ExtractAssociatedIcon($path) ;"); wcscat(lDialogString, L"\ $balloon.Icon = $icon ; \ $balloon.BalloonTipIcon = $IconType ; \ $balloon.BalloonTipText = $Message ; \ $balloon.BalloonTipTitle = $Title ; \ $balloon.Text = 'lalala' ; \ $balloon.Visible = $true ; \ $balloon.ShowBalloonTip(5000)};\ Show-BalloonTip"); if (aTitle && wcslen(aTitle)) { wcscat(lDialogString, L" -Title '"); wcscat(lDialogString, aTitle); wcscat(lDialogString, L"'"); } if (aMessage && wcslen(aMessage)) { wcscat(lDialogString, L" -Message '"); wcscat(lDialogString, aMessage); wcscat(lDialogString, L"'"); } if (aMessage && wcslen(aIconType)) { wcscat(lDialogString, L" -IconType '"); wcscat(lDialogString, aIconType); wcscat(lDialogString, L"'"); } wcscat(lDialogString, L"\""); /* wprintf ( L"lDialogString: %ls\n" , lDialogString ) ; */ hiddenConsoleW(lDialogString, aTitle, 0); free(lDialogString); return 1; } static int notifyWinGui( char const * const aTitle, /* NULL or "" */ char const * const aMessage, /* NULL or "" may NOT contain \n nor \t */ char const * const aIconType) { wchar_t * lTitle; wchar_t * lMessage; wchar_t * lIconType; if (tinyfd_winUtf8) { lTitle = utf8to16(aTitle); lMessage = utf8to16(aMessage); lIconType = utf8to16(aIconType); } else { lTitle = mbcsTo16(aTitle); lMessage = mbcsTo16(aMessage); lIconType = mbcsTo16(aIconType); } tinyfd_notifyPopupW( lTitle, lMessage, lIconType); free(lTitle); free(lMessage); free(lIconType); return 1; } wchar_t const * tinyfd_inputBoxW( wchar_t const * const aTitle, /* NULL or L"" */ wchar_t const * const aMessage, /* NULL or L"" may NOT contain \n nor \t */ wchar_t const * const aDefaultInput) /* L"" , if NULL it's a passwordBox */ { static wchar_t lBuff[MAX_PATH_OR_CMD]; wchar_t * lDialogString; FILE * lIn; FILE * lFile; int lResult; size_t lTitleLen; size_t lMessageLen; size_t lDialogStringLen; if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return (wchar_t const *)1; } lTitleLen = aTitle ? wcslen(aTitle) : 0 ; lMessageLen = aMessage ? wcslen(aMessage) : 0 ; lDialogStringLen = 3 * MAX_PATH_OR_CMD + lTitleLen + lMessageLen; lDialogString = (wchar_t *)malloc(2 * lDialogStringLen); if (aDefaultInput) { #if !defined(AVOID_EXECUTION) swprintf(lDialogString, #if !defined(__BORLANDC__) && !defined(__TINYC__) && ( !defined(__GNUC__) || (__GNUC__) >= 5 ) lDialogStringLen, #endif L"%ls\\AppData\\Local\\Temp\\tinyfd.vbs", _wgetenv(L"USERPROFILE")); #endif } else { #if !defined(AVOID_EXECUTION) swprintf(lDialogString, #if !defined(__BORLANDC__) && !defined(__TINYC__) && ( !defined(__GNUC__) || (__GNUC__) >= 5 ) lDialogStringLen, #endif L"%ls\\AppData\\Local\\Temp\\tinyfd.hta", _wgetenv(L"USERPROFILE")); #endif } lIn = _wfopen(lDialogString, L"w"); if (!lIn) { free(lDialogString); return NULL; } if ( aDefaultInput ) { wcscpy(lDialogString, L"Dim result:result=InputBox(\""); if (aMessage && wcslen(aMessage)) { wcscpy(lBuff, aMessage); replaceWchar(lBuff, L'\n', L' '); wcscat(lDialogString, lBuff); } wcscat(lDialogString, L"\",\"tinyfiledialogsTopWindow\",\""); if (aDefaultInput && wcslen(aDefaultInput)) { wcscpy(lBuff, aDefaultInput); replaceWchar(lBuff, L'\n', L' '); wcscat(lDialogString, lBuff); } wcscat(lDialogString, L"\"):If IsEmpty(result) then:WScript.Echo 0"); wcscat(lDialogString, L":Else: WScript.Echo \"1\" & result : End If"); } else { wcscpy(lDialogString, L"\n\ \n\ \n\ "); wcscat(lDialogString, L"tinyfiledialogsTopWindow"); wcscat(lDialogString, L"\n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\
\n"); wcscat(lDialogString, aMessage ? aMessage : L""); wcscat(lDialogString, L"\n\ \n\ \n\ \n\
\n\

\n\ \n\
\n\
\n"); wcscat(lDialogString, L"\n\ \n\ \n\ \n\
\n\
\n\
\n\ \n\ \n\ " ) ; } fputws(lDialogString, lIn); fclose(lIn); if (aDefaultInput) { #if !defined(AVOID_EXECUTION) swprintf(lDialogString, #if !defined(__BORLANDC__) && !defined(__TINYC__) && ( !defined(__GNUC__) || (__GNUC__) >= 5 ) lDialogStringLen, #endif L"%ls\\AppData\\Local\\Temp\\tinyfd.txt",_wgetenv(L"USERPROFILE")); #endif #ifdef TINYFD_NOCCSUNICODE lFile = _wfopen(lDialogString, L"w"); fputc(0xFF, lFile); fputc(0xFE, lFile); #else lFile = _wfopen(lDialogString, L"wt, ccs=UNICODE"); /*or ccs=UTF-16LE*/ #endif fclose(lFile); wcscpy(lDialogString, L"cmd.exe /c cscript.exe //U //Nologo "); wcscat(lDialogString, L"\"%USERPROFILE%\\AppData\\Local\\Temp\\tinyfd.vbs\" "); wcscat(lDialogString, L">> \"%USERPROFILE%\\AppData\\Local\\Temp\\tinyfd.txt\""); } else { wcscpy(lDialogString, L"cmd.exe /c mshta.exe \"%USERPROFILE%\\AppData\\Local\\Temp\\tinyfd.hta\""); } /* wprintf ( "lDialogString: %ls\n" , lDialogString ) ; */ hiddenConsoleW(lDialogString, aTitle, 1); #if !defined(AVOID_EXECUTION) swprintf(lDialogString, #if !defined(__BORLANDC__) && !defined(__TINYC__) && ( !defined(__GNUC__) || (__GNUC__) >= 5 ) lDialogStringLen, #endif L"%ls\\AppData\\Local\\Temp\\tinyfd.txt", _wgetenv(L"USERPROFILE")); /* wprintf(L"lDialogString: %ls\n", lDialogString); */ #endif #ifdef TINYFD_NOCCSUNICODE if (!(lIn = _wfopen(lDialogString, L"r"))) #else if (!(lIn = _wfopen(lDialogString, L"rt, ccs=UNICODE"))) /*or ccs=UTF-16LE*/ #endif { _wremove(lDialogString); free(lDialogString); return NULL; } memset(lBuff, 0, MAX_PATH_OR_CMD * sizeof(wchar_t) ); #ifdef TINYFD_NOCCSUNICODE fgets((char *)lBuff, 2*MAX_PATH_OR_CMD, lIn); #else fgetws(lBuff, MAX_PATH_OR_CMD, lIn); #endif fclose(lIn); wipefileW(lDialogString); _wremove(lDialogString); if (aDefaultInput) { #if !defined(AVOID_EXECUTION) swprintf(lDialogString, #if !defined(__BORLANDC__) && !defined(__TINYC__) && ( !defined(__GNUC__) || (__GNUC__) >= 5 ) lDialogStringLen, #endif L"%ls\\AppData\\Local\\Temp\\tinyfd.vbs", _wgetenv(L"USERPROFILE")); #endif } else { #if !defined(AVOID_EXECUTION) swprintf(lDialogString, #if !defined(__BORLANDC__) && !defined(__TINYC__) && ( !defined(__GNUC__) || (__GNUC__) >= 5 ) lDialogStringLen, #endif L"%ls\\AppData\\Local\\Temp\\tinyfd.hta", _wgetenv(L"USERPROFILE")); #endif } _wremove(lDialogString); free(lDialogString); /* wprintf( L"lBuff: %ls\n" , lBuff ) ; */ #ifdef TINYFD_NOCCSUNICODE lResult = !wcsncmp(lBuff+1, L"1", 1); #else lResult = !wcsncmp(lBuff, L"1", 1); #endif /* printf( "lResult: %d \n" , lResult ) ; */ if (!lResult) { return NULL ; } /* wprintf( "lBuff+1: %ls\n" , lBuff+1 ) ; */ #ifdef TINYFD_NOCCSUNICODE if (aDefaultInput) { lDialogStringLen = wcslen(lBuff) ; lBuff[lDialogStringLen - 1] = L'\0'; lBuff[lDialogStringLen - 2] = L'\0'; } return lBuff + 2; #else if (aDefaultInput) lBuff[wcslen(lBuff) - 1] = L'\0'; return lBuff + 1; #endif } static char const * inputBoxWinGui( char * const aoBuff, char const * const aTitle, /* NULL or "" */ char const * const aMessage, /* NULL or "" may NOT contain \n nor \t */ char const * const aDefaultInput) /* "" , if NULL it's a passwordBox */ { wchar_t * lTitle; wchar_t * lMessage; wchar_t * lDefaultInput; wchar_t const * lTmpWChar; char * lTmpChar; if (tinyfd_winUtf8) { lTitle = utf8to16(aTitle); lMessage = utf8to16(aMessage); lDefaultInput = utf8to16(aDefaultInput); } else { lTitle = mbcsTo16(aTitle); lMessage = mbcsTo16(aMessage); lDefaultInput = mbcsTo16(aDefaultInput); } lTmpWChar = tinyfd_inputBoxW( lTitle, lMessage, lDefaultInput); free(lTitle); free(lMessage); free(lDefaultInput); if (!lTmpWChar) { return NULL; } if (tinyfd_winUtf8) { lTmpChar = utf16to8(lTmpWChar); } else { lTmpChar = utf16toMbcs(lTmpWChar); } strcpy(aoBuff, lTmpChar); free(lTmpChar); return aoBuff; } wchar_t const * tinyfd_saveFileDialogW( wchar_t const * const aTitle, /* NULL or "" */ wchar_t const * const aDefaultPathAndFile, /* NULL or "" */ int const aNumOfFilterPatterns, /* 0 */ wchar_t const * const * const aFilterPatterns, /* NULL or {"*.jpg","*.png"} */ wchar_t const * const aSingleFilterDescription) /* NULL or "image files" */ { static wchar_t lBuff[MAX_PATH_OR_CMD]; wchar_t lDirname[MAX_PATH_OR_CMD]; wchar_t lDialogString[MAX_PATH_OR_CMD]; wchar_t lFilterPatterns[MAX_PATH_OR_CMD] = L""; wchar_t * p; wchar_t * lRetval; wchar_t const * ldefExt = NULL; int i; HRESULT lHResult; OPENFILENAMEW ofn = {0}; if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return (wchar_t const *)1; } lHResult = CoInitializeEx(NULL, 0); getPathWithoutFinalSlashW(lDirname, aDefaultPathAndFile); getLastNameW(lBuff, aDefaultPathAndFile); if (aNumOfFilterPatterns > 0) { ldefExt = aFilterPatterns[0]; if (aSingleFilterDescription && wcslen(aSingleFilterDescription)) { wcscpy(lFilterPatterns, aSingleFilterDescription); wcscat(lFilterPatterns, L"\n"); } wcscat(lFilterPatterns, aFilterPatterns[0]); for (i = 1; i < aNumOfFilterPatterns; i++) { wcscat(lFilterPatterns, L";"); wcscat(lFilterPatterns, aFilterPatterns[i]); } wcscat(lFilterPatterns, L"\n"); if (!(aSingleFilterDescription && wcslen(aSingleFilterDescription))) { wcscpy(lDialogString, lFilterPatterns); wcscat(lFilterPatterns, lDialogString); } wcscat(lFilterPatterns, L"All Files\n*.*\n"); p = lFilterPatterns; while ((p = wcschr(p, L'\n')) != NULL) { *p = L'\0'; p++; } } ofn.lStructSize = sizeof(OPENFILENAMEW); ofn.hwndOwner = GetForegroundWindow(); ofn.hInstance = 0; ofn.lpstrFilter = wcslen(lFilterPatterns) ? lFilterPatterns : NULL; ofn.lpstrCustomFilter = NULL; ofn.nMaxCustFilter = 0; ofn.nFilterIndex = 1; ofn.lpstrFile = lBuff; ofn.nMaxFile = MAX_PATH_OR_CMD; ofn.lpstrFileTitle = NULL; ofn.nMaxFileTitle = MAX_PATH_OR_CMD/2; ofn.lpstrInitialDir = wcslen(lDirname) ? lDirname : NULL; ofn.lpstrTitle = aTitle && wcslen(aTitle) ? aTitle : NULL; ofn.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST ; ofn.nFileOffset = 0; ofn.nFileExtension = 0; ofn.lpstrDefExt = ldefExt; ofn.lCustData = 0L; ofn.lpfnHook = NULL; ofn.lpTemplateName = NULL; if (GetSaveFileNameW(&ofn) == 0) { lRetval = NULL; } else { lRetval = lBuff; } if (lHResult == S_OK || lHResult == S_FALSE) { CoUninitialize(); } return lRetval; } static char const * saveFileDialogWinGui8( char * const aoBuff, char const * const aTitle, /* NULL or "" */ char const * const aDefaultPathAndFile, /* NULL or "" */ int const aNumOfFilterPatterns, /* 0 */ char const * const * const aFilterPatterns, /* NULL or {"*.jpg","*.png"} */ char const * const aSingleFilterDescription) /* NULL or "image files" */ { wchar_t * lTitle; wchar_t * lDefaultPathAndFile; wchar_t * lSingleFilterDescription; wchar_t * * lFilterPatterns; wchar_t const * lTmpWChar; char * lTmpChar; int i ; lFilterPatterns = (wchar_t **) malloc(aNumOfFilterPatterns*sizeof(wchar_t *)); for (i = 0; i < aNumOfFilterPatterns; i++) { lFilterPatterns[i] = utf8to16(aFilterPatterns[i]); } lTitle = utf8to16(aTitle); lDefaultPathAndFile = utf8to16(aDefaultPathAndFile); lSingleFilterDescription = utf8to16(aSingleFilterDescription); lTmpWChar = tinyfd_saveFileDialogW( lTitle, lDefaultPathAndFile, aNumOfFilterPatterns, (wchar_t const** ) /*stupid cast for gcc*/ lFilterPatterns, lSingleFilterDescription); free(lTitle); free(lDefaultPathAndFile); free(lSingleFilterDescription); for (i = 0; i < aNumOfFilterPatterns; i++) { free(lFilterPatterns[i]); } free(lFilterPatterns); if (!lTmpWChar) { return NULL; } lTmpChar = utf16to8(lTmpWChar); strcpy(aoBuff, lTmpChar); free(lTmpChar); return aoBuff; } wchar_t const * tinyfd_openFileDialogW( wchar_t const * const aTitle, /* NULL or "" */ wchar_t const * const aDefaultPathAndFile, /* NULL or "" */ int const aNumOfFilterPatterns, /* 0 */ wchar_t const * const * const aFilterPatterns, /* NULL or {"*.jpg","*.png"} */ wchar_t const * const aSingleFilterDescription, /* NULL or "image files" */ int const aAllowMultipleSelects) /* 0 or 1 */ { static wchar_t lBuff[MAX_MULTIPLE_FILES*MAX_PATH_OR_CMD]; size_t lLengths[MAX_MULTIPLE_FILES]; wchar_t lDirname[MAX_PATH_OR_CMD]; wchar_t lFilterPatterns[MAX_PATH_OR_CMD] = L""; wchar_t lDialogString[MAX_PATH_OR_CMD]; wchar_t * lPointers[MAX_MULTIPLE_FILES]; wchar_t * lRetval, * p; int i, j; size_t lBuffLen; HRESULT lHResult; OPENFILENAMEW ofn = { 0 }; if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return (wchar_t const *)1; } lHResult = CoInitializeEx(NULL, 0); getPathWithoutFinalSlashW(lDirname, aDefaultPathAndFile); getLastNameW(lBuff, aDefaultPathAndFile); if (aNumOfFilterPatterns > 0) { if (aSingleFilterDescription && wcslen(aSingleFilterDescription)) { wcscpy(lFilterPatterns, aSingleFilterDescription); wcscat(lFilterPatterns, L"\n"); } wcscat(lFilterPatterns, aFilterPatterns[0]); for (i = 1; i < aNumOfFilterPatterns; i++) { wcscat(lFilterPatterns, L";"); wcscat(lFilterPatterns, aFilterPatterns[i]); } wcscat(lFilterPatterns, L"\n"); if (!(aSingleFilterDescription && wcslen(aSingleFilterDescription))) { wcscpy(lDialogString, lFilterPatterns); wcscat(lFilterPatterns, lDialogString); } wcscat(lFilterPatterns, L"All Files\n*.*\n"); p = lFilterPatterns; while ((p = wcschr(p, L'\n')) != NULL) { *p = L'\0'; p++; } } ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = GetForegroundWindow(); ofn.hInstance = 0; ofn.lpstrFilter = wcslen(lFilterPatterns) ? lFilterPatterns : NULL; ofn.lpstrCustomFilter = NULL; ofn.nMaxCustFilter = 0; ofn.nFilterIndex = 1; ofn.lpstrFile = lBuff; ofn.nMaxFile = MAX_PATH_OR_CMD; ofn.lpstrFileTitle = NULL; ofn.nMaxFileTitle = MAX_PATH_OR_CMD / 2; ofn.lpstrInitialDir = wcslen(lDirname) ? lDirname : NULL; ofn.lpstrTitle = aTitle && wcslen(aTitle) ? aTitle : NULL; ofn.Flags = OFN_EXPLORER | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; ofn.nFileOffset = 0; ofn.nFileExtension = 0; ofn.lpstrDefExt = NULL; ofn.lCustData = 0L; ofn.lpfnHook = NULL; ofn.lpTemplateName = NULL; if (aAllowMultipleSelects) { ofn.Flags |= OFN_ALLOWMULTISELECT; } if (GetOpenFileNameW(&ofn) == 0) { lRetval = NULL; } else { lBuffLen = wcslen(lBuff); lPointers[0] = lBuff + lBuffLen + 1; if (!aAllowMultipleSelects || (lPointers[0][0] == L'\0')) { lRetval = lBuff; } else { i = 0; do { lLengths[i] = wcslen(lPointers[i]); lPointers[i + 1] = lPointers[i] + lLengths[i] + 1; i++; } while (lPointers[i][0] != L'\0'); i--; p = lBuff + MAX_MULTIPLE_FILES*MAX_PATH_OR_CMD - 1; *p = L'\0'; for (j = i; j >= 0; j--) { p -= lLengths[j]; memmove(p, lPointers[j], lLengths[j]*sizeof(wchar_t)); p--; *p = L'\\'; p -= lBuffLen; memmove(p, lBuff, lBuffLen*sizeof(wchar_t)); p--; *p = L'|'; } p++; lRetval = p; } } if (lHResult == S_OK || lHResult == S_FALSE) { CoUninitialize(); } return lRetval; } static char const * openFileDialogWinGui8( char * const aoBuff, char const * const aTitle, /* NULL or "" */ char const * const aDefaultPathAndFile, /* NULL or "" */ int const aNumOfFilterPatterns, /* 0 */ char const * const * const aFilterPatterns, /* NULL or {"*.jpg","*.png"} */ char const * const aSingleFilterDescription, /* NULL or "image files" */ int const aAllowMultipleSelects) /* 0 or 1 */ { wchar_t * lTitle; wchar_t * lDefaultPathAndFile; wchar_t * lSingleFilterDescription; wchar_t * * lFilterPatterns; wchar_t const * lTmpWChar; char * lTmpChar; int i; lFilterPatterns = (wchar_t * *) malloc(aNumOfFilterPatterns*sizeof(wchar_t *)); for (i = 0; i < aNumOfFilterPatterns; i++) { lFilterPatterns[i] = utf8to16(aFilterPatterns[i]); } lTitle = utf8to16(aTitle); lDefaultPathAndFile = utf8to16(aDefaultPathAndFile); lSingleFilterDescription = utf8to16(aSingleFilterDescription); lTmpWChar = tinyfd_openFileDialogW( lTitle, lDefaultPathAndFile, aNumOfFilterPatterns, (wchar_t const**) /*stupid cast for gcc*/ lFilterPatterns, lSingleFilterDescription, aAllowMultipleSelects); free(lTitle); free(lDefaultPathAndFile); free(lSingleFilterDescription); for (i = 0; i < aNumOfFilterPatterns; i++) { free(lFilterPatterns[i]); } free(lFilterPatterns); if (!lTmpWChar) { return NULL; } lTmpChar = utf16to8(lTmpWChar); strcpy(aoBuff, lTmpChar); free(lTmpChar); return aoBuff; } #ifndef TINYFD_NOSELECTFOLDERWIN BOOL CALLBACK BrowseCallbackProc_enum(HWND hWndChild, LPARAM lParam) { char buf[255]; GetClassNameA(hWndChild, buf, sizeof(buf)); if (strcmp(buf, "SysTreeView32") == 0) { HTREEITEM hNode = TreeView_GetSelection(hWndChild); TreeView_EnsureVisible(hWndChild, hNode); return FALSE; } return TRUE; } BOOL CALLBACK BrowseCallbackProcW_enum(HWND hWndChild, LPARAM lParam) { wchar_t buf[255]; GetClassNameW(hWndChild, buf, sizeof(buf)); if (wcscmp(buf, L"SysTreeView32") == 0) { HTREEITEM hNode = TreeView_GetSelection(hWndChild); TreeView_EnsureVisible(hWndChild, hNode); return FALSE; } return TRUE; } static int __stdcall BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData) { switch (uMsg) { case BFFM_INITIALIZED: SendMessage(hwnd, BFFM_SETSELECTION, TRUE, pData); break; case BFFM_SELCHANGED: EnumChildWindows(hwnd, BrowseCallbackProc_enum, 0); } return 0; } static int __stdcall BrowseCallbackProcW(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData) { switch (uMsg) { case BFFM_INITIALIZED: SendMessage(hwnd, BFFM_SETSELECTIONW, TRUE, (LPARAM)pData); break; case BFFM_SELCHANGED: EnumChildWindows(hwnd, BrowseCallbackProcW_enum, 0); } return 0; } wchar_t const * tinyfd_selectFolderDialogW( wchar_t const * const aTitle, /* NULL or "" */ wchar_t const * const aDefaultPath) /* NULL or "" */ { static wchar_t lBuff[MAX_PATH_OR_CMD]; wchar_t * lRetval; BROWSEINFOW bInfo; LPITEMIDLIST lpItem; HRESULT lHResult; if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return (wchar_t const *)1; } lHResult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); bInfo.hwndOwner = GetForegroundWindow(); bInfo.pidlRoot = NULL; bInfo.pszDisplayName = lBuff; bInfo.lpszTitle = aTitle && wcslen(aTitle) ? aTitle : NULL; if (lHResult == S_OK || lHResult == S_FALSE) { bInfo.ulFlags = BIF_USENEWUI; } bInfo.lpfn = BrowseCallbackProcW; bInfo.lParam = (LPARAM)aDefaultPath; bInfo.iImage = -1; lpItem = SHBrowseForFolderW(&bInfo); if (!lpItem) { lRetval = NULL; } else { SHGetPathFromIDListW(lpItem, lBuff); lRetval = lBuff ; } if (lHResult == S_OK || lHResult == S_FALSE) { CoUninitialize(); } return lRetval; } static char const * selectFolderDialogWinGui8( char * const aoBuff , char const * const aTitle , /* NULL or "" */ char const * const aDefaultPath ) /* NULL or "" */ { wchar_t * lTitle; wchar_t * lDefaultPath; wchar_t const * lTmpWChar; char * lTmpChar; lTitle = utf8to16(aTitle); lDefaultPath = utf8to16(aDefaultPath); lTmpWChar = tinyfd_selectFolderDialogW( lTitle, lDefaultPath); free(lTitle); free(lDefaultPath); if (!lTmpWChar) { return NULL; } lTmpChar = utf16to8(lTmpWChar); strcpy(aoBuff, lTmpChar); free(lTmpChar); return aoBuff; } #endif /*TINYFD_NOSELECTFOLDERWIN*/ wchar_t const * tinyfd_colorChooserW( wchar_t const * const aTitle, /* NULL or "" */ wchar_t const * const aDefaultHexRGB, /* NULL or "#FF0000"*/ unsigned char const aDefaultRGB[3], /* { 0 , 255 , 255 } */ unsigned char aoResultRGB[3]) /* { 0 , 0 , 0 } */ { static wchar_t lResultHexRGB[8]; CHOOSECOLORW cc; COLORREF crCustColors[16]; unsigned char lDefaultRGB[3]; int lRet; HRESULT lHResult; if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return (wchar_t const *)1; } lHResult = CoInitializeEx(NULL, 0); if (aDefaultHexRGB) { Hex2RGBW(aDefaultHexRGB, lDefaultRGB); } else { lDefaultRGB[0] = aDefaultRGB[0]; lDefaultRGB[1] = aDefaultRGB[1]; lDefaultRGB[2] = aDefaultRGB[2]; } /* we can't use aTitle */ cc.lStructSize = sizeof(CHOOSECOLOR); cc.hwndOwner = GetForegroundWindow(); cc.hInstance = NULL; cc.rgbResult = RGB(lDefaultRGB[0], lDefaultRGB[1], lDefaultRGB[2]); cc.lpCustColors = crCustColors; cc.Flags = CC_RGBINIT | CC_FULLOPEN | CC_ANYCOLOR ; cc.lCustData = 0; cc.lpfnHook = NULL; cc.lpTemplateName = NULL; lRet = ChooseColorW(&cc); if (!lRet) { return NULL; } aoResultRGB[0] = GetRValue(cc.rgbResult); aoResultRGB[1] = GetGValue(cc.rgbResult); aoResultRGB[2] = GetBValue(cc.rgbResult); RGB2HexW(aoResultRGB, lResultHexRGB); if (lHResult == S_OK || lHResult == S_FALSE) { CoUninitialize(); } return lResultHexRGB; } static char const * colorChooserWinGui8( char const * const aTitle, /* NULL or "" */ char const * const aDefaultHexRGB, /* NULL or "#FF0000"*/ unsigned char const aDefaultRGB[3], /* { 0 , 255 , 255 } */ unsigned char aoResultRGB[3]) /* { 0 , 0 , 0 } */ { static char lResultHexRGB[8]; wchar_t * lTitle; wchar_t * lDefaultHexRGB; wchar_t const * lTmpWChar; char * lTmpChar; lTitle = utf8to16(aTitle); lDefaultHexRGB = utf8to16(aDefaultHexRGB); lTmpWChar = tinyfd_colorChooserW( lTitle, lDefaultHexRGB, aDefaultRGB, aoResultRGB ); free(lTitle); free(lDefaultHexRGB); if (!lTmpWChar) { return NULL; } lTmpChar = utf16to8(lTmpWChar); strcpy(lResultHexRGB, lTmpChar); free(lTmpChar); return lResultHexRGB; } static int messageBoxWinGuiA( char const * const aTitle , /* NULL or "" */ char const * const aMessage , /* NULL or "" may contain \n and \t */ char const * const aDialogType , /* "ok" "okcancel" "yesno" "yesnocancel" */ char const * const aIconType , /* "info" "warning" "error" "question" */ int const aDefaultButton ) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */ { int lBoxReturnValue; UINT aCode ; if ( aIconType && ! strcmp( "warning" , aIconType ) ) { aCode = MB_ICONWARNING ; } else if ( aIconType && ! strcmp("error", aIconType)) { aCode = MB_ICONERROR ; } else if ( aIconType && ! strcmp("question", aIconType)) { aCode = MB_ICONQUESTION ; } else { aCode = MB_ICONINFORMATION ; } if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) ) { aCode += MB_OKCANCEL ; if ( ! aDefaultButton ) { aCode += MB_DEFBUTTON2 ; } } else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) ) { aCode += MB_YESNO ; if ( ! aDefaultButton ) { aCode += MB_DEFBUTTON2 ; } } else if (aDialogType && !strcmp("yesnocancel", aDialogType)) { aCode += MB_YESNOCANCEL; if (!aDefaultButton) { aCode += MB_DEFBUTTON3; } else if (aDefaultButton == 2) { aCode += MB_DEFBUTTON2; } } else { aCode += MB_OK ; } aCode += MB_TOPMOST; lBoxReturnValue = MessageBoxA(GetForegroundWindow(), aMessage, aTitle, aCode); if (((aDialogType && !strcmp("yesnocancel", aDialogType)) && (lBoxReturnValue == IDNO))) { return 2; } if ( ( ( aDialogType && strcmp("yesnocancel", aDialogType) && strcmp("okcancel", aDialogType) && strcmp("yesno", aDialogType))) || (lBoxReturnValue == IDOK) || (lBoxReturnValue == IDYES) ) { return 1 ; } else { return 0 ; } } static char const * saveFileDialogWinGuiA( char * const aoBuff , char const * const aTitle , /* NULL or "" */ char const * const aDefaultPathAndFile , /* NULL or "" */ int const aNumOfFilterPatterns , /* 0 */ char const * const * const aFilterPatterns , /* NULL or {"*.jpg","*.png"} */ char const * const aSingleFilterDescription ) /* NULL or "image files" */ { char lDirname [MAX_PATH_OR_CMD] ; char lDialogString[MAX_PATH_OR_CMD]; char lFilterPatterns[MAX_PATH_OR_CMD] = ""; int i ; char * p; char * lRetval; HRESULT lHResult; char const * ldefExt = NULL; OPENFILENAMEA ofn = { 0 }; lHResult = CoInitializeEx(NULL,0); getPathWithoutFinalSlash(lDirname, aDefaultPathAndFile); getLastName(aoBuff, aDefaultPathAndFile); if (aNumOfFilterPatterns > 0) { ldefExt = aFilterPatterns[0]; if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) { strcpy(lFilterPatterns, aSingleFilterDescription); strcat(lFilterPatterns, "\n"); } strcat(lFilterPatterns, aFilterPatterns[0]); for (i = 1; i < aNumOfFilterPatterns; i++) { strcat(lFilterPatterns, ";"); strcat(lFilterPatterns, aFilterPatterns[i]); } strcat(lFilterPatterns, "\n"); if ( ! (aSingleFilterDescription && strlen(aSingleFilterDescription) ) ) { strcpy(lDialogString, lFilterPatterns); strcat(lFilterPatterns, lDialogString); } strcat(lFilterPatterns, "All Files\n*.*\n"); p = lFilterPatterns; while ((p = strchr(p, '\n')) != NULL) { *p = '\0'; p ++ ; } } ofn.lStructSize = sizeof(OPENFILENAME) ; ofn.hwndOwner = GetForegroundWindow(); ofn.hInstance = 0 ; ofn.lpstrFilter = strlen(lFilterPatterns) ? lFilterPatterns : NULL; ofn.lpstrCustomFilter = NULL ; ofn.nMaxCustFilter = 0 ; ofn.nFilterIndex = 1 ; ofn.lpstrFile = aoBuff; ofn.nMaxFile = MAX_PATH_OR_CMD ; ofn.lpstrFileTitle = NULL ; ofn.nMaxFileTitle = MAX_PATH_OR_CMD / 2; ofn.lpstrInitialDir = strlen(lDirname) ? lDirname : NULL; ofn.lpstrTitle = aTitle && strlen(aTitle) ? aTitle : NULL; ofn.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR ; ofn.nFileOffset = 0 ; ofn.nFileExtension = 0 ; ofn.lpstrDefExt = ldefExt; ofn.lCustData = 0L ; ofn.lpfnHook = NULL ; ofn.lpTemplateName = NULL ; if ( GetSaveFileNameA ( & ofn ) == 0 ) { lRetval = NULL ; } else { lRetval = aoBuff ; } if (lHResult==S_OK || lHResult==S_FALSE) { CoUninitialize(); } return lRetval ; } static char const * openFileDialogWinGuiA( char * const aoBuff , char const * const aTitle , /* NULL or "" */ char const * const aDefaultPathAndFile , /* NULL or "" */ int const aNumOfFilterPatterns , /* 0 */ char const * const * const aFilterPatterns , /* NULL or {"*.jpg","*.png"} */ char const * const aSingleFilterDescription , /* NULL or "image files" */ int const aAllowMultipleSelects ) /* 0 or 1 */ { char lDirname [MAX_PATH_OR_CMD] ; char lFilterPatterns[MAX_PATH_OR_CMD] = ""; char lDialogString[MAX_PATH_OR_CMD] ; char * lPointers[MAX_MULTIPLE_FILES]; size_t lLengths[MAX_MULTIPLE_FILES]; int i , j ; char * p; size_t lBuffLen ; char * lRetval; HRESULT lHResult; OPENFILENAMEA ofn = {0}; lHResult = CoInitializeEx(NULL,0); getPathWithoutFinalSlash(lDirname, aDefaultPathAndFile); getLastName(aoBuff, aDefaultPathAndFile); if (aNumOfFilterPatterns > 0) { if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) { strcpy(lFilterPatterns, aSingleFilterDescription); strcat(lFilterPatterns, "\n"); } strcat(lFilterPatterns, aFilterPatterns[0]); for (i = 1; i < aNumOfFilterPatterns; i++) { strcat(lFilterPatterns, ";"); strcat(lFilterPatterns, aFilterPatterns[i]); } strcat(lFilterPatterns, "\n"); if ( ! (aSingleFilterDescription && strlen(aSingleFilterDescription) ) ) { strcpy(lDialogString, lFilterPatterns); strcat(lFilterPatterns, lDialogString); } strcat(lFilterPatterns, "All Files\n*.*\n"); p = lFilterPatterns; while ((p = strchr(p, '\n')) != NULL) { *p = '\0'; p ++ ; } } ofn.lStructSize = sizeof( OPENFILENAME ) ; ofn.hwndOwner = GetForegroundWindow(); ofn.hInstance = 0 ; ofn.lpstrFilter = strlen(lFilterPatterns) ? lFilterPatterns : NULL; ofn.lpstrCustomFilter = NULL ; ofn.nMaxCustFilter = 0 ; ofn.nFilterIndex = 1 ; ofn.lpstrFile = aoBuff ; ofn.nMaxFile = MAX_PATH_OR_CMD ; ofn.lpstrFileTitle = NULL ; ofn.nMaxFileTitle = MAX_PATH_OR_CMD / 2; ofn.lpstrInitialDir = strlen(lDirname) ? lDirname : NULL; ofn.lpstrTitle = aTitle && strlen(aTitle) ? aTitle : NULL; ofn.Flags = OFN_EXPLORER | OFN_NOCHANGEDIR ; ofn.nFileOffset = 0 ; ofn.nFileExtension = 0 ; ofn.lpstrDefExt = NULL ; ofn.lCustData = 0L ; ofn.lpfnHook = NULL ; ofn.lpTemplateName = NULL ; if ( aAllowMultipleSelects ) { ofn.Flags |= OFN_ALLOWMULTISELECT; } if ( GetOpenFileNameA( & ofn ) == 0 ) { lRetval = NULL ; } else { lBuffLen = strlen(aoBuff) ; lPointers[0] = aoBuff + lBuffLen + 1 ; if ( !aAllowMultipleSelects || (lPointers[0][0] == '\0') ) { lRetval = aoBuff ; } else { i = 0 ; do { lLengths[i] = strlen(lPointers[i]); lPointers[i+1] = lPointers[i] + lLengths[i] + 1 ; i ++ ; } while ( lPointers[i][0] != '\0' ); i--; p = aoBuff + MAX_MULTIPLE_FILES*MAX_PATH_OR_CMD - 1 ; * p = '\0'; for ( j = i ; j >=0 ; j-- ) { p -= lLengths[j]; memmove(p, lPointers[j], lLengths[j]); p--; *p = '\\'; p -= lBuffLen ; memmove(p, aoBuff, lBuffLen); p--; *p = '|'; } p++; lRetval = p ; } } if (lHResult==S_OK || lHResult==S_FALSE) { CoUninitialize(); } return lRetval; } #ifndef TINYFD_NOSELECTFOLDERWIN static char const * selectFolderDialogWinGuiA( char * const aoBuff , char const * const aTitle , /* NULL or "" */ char const * const aDefaultPath ) /* NULL or "" */ { BROWSEINFOA bInfo ; LPITEMIDLIST lpItem ; HRESULT lHResult ; char * lRetval = NULL ; lHResult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); /* we can't use aDefaultPath */ bInfo.hwndOwner = GetForegroundWindow(); bInfo.pidlRoot = NULL ; bInfo.pszDisplayName = aoBuff ; bInfo.lpszTitle = aTitle && strlen(aTitle) ? aTitle : NULL; if (lHResult == S_OK || lHResult == S_FALSE) { bInfo.ulFlags = BIF_USENEWUI; } bInfo.lpfn = BrowseCallbackProc; bInfo.lParam = (LPARAM)aDefaultPath; bInfo.iImage = -1 ; lpItem = SHBrowseForFolderA( & bInfo ) ; if ( lpItem ) { SHGetPathFromIDListA( lpItem , aoBuff ) ; lRetval = aoBuff; } if (lHResult==S_OK || lHResult==S_FALSE) { CoUninitialize(); } return lRetval; } #endif /*TINYFD_NOSELECTFOLDERWIN*/ static char const * colorChooserWinGuiA( char const * const aTitle, /* NULL or "" */ char const * const aDefaultHexRGB, /* NULL or "#FF0000"*/ unsigned char const aDefaultRGB[3], /* { 0 , 255 , 255 } */ unsigned char aoResultRGB[3]) /* { 0 , 0 , 0 } */ { static char lResultHexRGB[8]; CHOOSECOLORA cc; COLORREF crCustColors[16]; unsigned char lDefaultRGB[3]; int lRet; if ( aDefaultHexRGB ) { Hex2RGB(aDefaultHexRGB, lDefaultRGB); } else { lDefaultRGB[0]=aDefaultRGB[0]; lDefaultRGB[1]=aDefaultRGB[1]; lDefaultRGB[2]=aDefaultRGB[2]; } /* we can't use aTitle */ cc.lStructSize = sizeof( CHOOSECOLOR ) ; cc.hwndOwner = GetForegroundWindow(); cc.hInstance = NULL ; cc.rgbResult = RGB(lDefaultRGB[0], lDefaultRGB[1], lDefaultRGB[2]); cc.lpCustColors = crCustColors; cc.Flags = CC_RGBINIT | CC_FULLOPEN; cc.lCustData = 0; cc.lpfnHook = NULL; cc.lpTemplateName = NULL; lRet = ChooseColorA(&cc); if ( ! lRet ) { return NULL; } aoResultRGB[0] = GetRValue(cc.rgbResult); aoResultRGB[1] = GetGValue(cc.rgbResult); aoResultRGB[2] = GetBValue(cc.rgbResult); RGB2Hex(aoResultRGB, lResultHexRGB); return lResultHexRGB; } #endif /* TINYFD_NOLIB */ static int dialogPresent(void) { static int lDialogPresent = -1 ; char lBuff [MAX_PATH_OR_CMD] ; FILE * lIn ; char const * lString = "dialog.exe"; if ( lDialogPresent < 0 ) { #if !defined(AVOID_EXECUTION) if (!(lIn = _popen("where dialog.exe","r"))) { lDialogPresent = 0 ; return 0 ; } while ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) {} _pclose( lIn ) ; #endif if ( lBuff[strlen( lBuff ) -1] == '\n' ) { lBuff[strlen( lBuff ) -1] = '\0' ; } if ( strcmp(lBuff+strlen(lBuff)-strlen(lString),lString) ) { lDialogPresent = 0 ; } else { lDialogPresent = 1 ; } } return lDialogPresent; } static int messageBoxWinConsole( char const * const aTitle , /* NULL or "" */ char const * const aMessage , /* NULL or "" may contain \n and \t */ char const * const aDialogType , /* "ok" "okcancel" "yesno" "yesnocancel" */ char const * const aIconType , /* "info" "warning" "error" "question" */ int const aDefaultButton ) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */ { char lDialogString[MAX_PATH_OR_CMD]; char lDialogFile[MAX_PATH_OR_CMD]; FILE * lIn; char lBuff [MAX_PATH_OR_CMD] = ""; strcpy( lDialogString , "dialog " ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "--title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } if ( aDialogType && ( !strcmp( "okcancel" , aDialogType ) || !strcmp("yesno", aDialogType) || !strcmp("yesnocancel", aDialogType) ) ) { strcat(lDialogString, "--backtitle \"") ; strcat(lDialogString, "tab: move focus") ; strcat(lDialogString, "\" ") ; } if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) ) { if ( ! aDefaultButton ) { strcat( lDialogString , "--defaultno " ) ; } strcat( lDialogString , "--yes-label \"Ok\" --no-label \"Cancel\" --yesno " ) ; } else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) ) { if ( ! aDefaultButton ) { strcat( lDialogString , "--defaultno " ) ; } strcat( lDialogString , "--yesno " ) ; } else if (aDialogType && !strcmp("yesnocancel", aDialogType)) { if (!aDefaultButton) { strcat(lDialogString, "--defaultno "); } strcat(lDialogString, "--menu "); } else { strcat( lDialogString , "--msgbox " ) ; } strcat( lDialogString , "\"" ) ; if ( aMessage && strlen(aMessage) ) { replaceSubStr( aMessage , "\n" , "\\n" , lBuff ) ; strcat(lDialogString, lBuff) ; lBuff[0]='\0'; } strcat(lDialogString, "\" "); if (aDialogType && !strcmp("yesnocancel", aDialogType)) { strcat(lDialogString, "0 60 0 Yes \"\" No \"\""); strcat(lDialogString, "2>>"); } else { strcat(lDialogString, "10 60"); strcat(lDialogString, " && echo 1 > "); } strcpy(lDialogFile, getenv("USERPROFILE")); strcat(lDialogFile, "\\AppData\\Local\\Temp\\tinyfd.txt"); strcat(lDialogString, lDialogFile); /*if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ;*/ system( lDialogString ) ; if (!(lIn = fopen(lDialogFile, "r"))) { remove(lDialogFile); return 0 ; } while (fgets(lBuff, sizeof(lBuff), lIn) != NULL) {} fclose(lIn); remove(lDialogFile); if ( lBuff[strlen( lBuff ) -1] == '\n' ) { lBuff[strlen( lBuff ) -1] = '\0' ; } /* if (tinyfd_verbose) printf("lBuff: %s\n", lBuff); */ if ( ! strlen(lBuff) ) { return 0; } if (aDialogType && !strcmp("yesnocancel", aDialogType)) { if (lBuff[0] == 'Y') return 1; else return 2; } return 1; } static char const * inputBoxWinConsole( char * const aoBuff , char const * const aTitle , /* NULL or "" */ char const * const aMessage , /* NULL or "" may NOT contain \n nor \t */ char const * const aDefaultInput ) /* "" , if NULL it's a passwordBox */ { char lDialogString[MAX_PATH_OR_CMD]; char lDialogFile[MAX_PATH_OR_CMD]; FILE * lIn; int lResult; strcpy(lDialogFile, getenv("USERPROFILE")); strcat(lDialogFile, "\\AppData\\Local\\Temp\\tinyfd.txt"); strcpy(lDialogString , "echo|set /p=1 >" ) ; strcat(lDialogString, lDialogFile); strcat( lDialogString , " & " ) ; strcat( lDialogString , "dialog " ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "--title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } strcat(lDialogString, "--backtitle \"") ; strcat(lDialogString, "tab: move focus") ; if ( ! aDefaultInput ) { strcat(lDialogString, " (sometimes nothing, no blink nor star, is shown in text field)") ; } strcat(lDialogString, "\" ") ; if ( ! aDefaultInput ) { strcat( lDialogString , "--insecure --passwordbox" ) ; } else { strcat( lDialogString , "--inputbox" ) ; } strcat( lDialogString , " \"" ) ; if ( aMessage && strlen(aMessage) ) { strcat(lDialogString, aMessage) ; } strcat(lDialogString,"\" 10 60 ") ; if ( aDefaultInput && strlen(aDefaultInput) ) { strcat(lDialogString, "\"") ; strcat(lDialogString, aDefaultInput) ; strcat(lDialogString, "\" ") ; } strcat(lDialogString, "2>>"); strcpy(lDialogFile, getenv("USERPROFILE")); strcat(lDialogFile, "\\AppData\\Local\\Temp\\tinyfd.txt"); strcat(lDialogString, lDialogFile); strcat(lDialogString, " || echo 0 > "); strcat(lDialogString, lDialogFile); /* printf( "lDialogString: %s\n" , lDialogString ) ; */ system( lDialogString ) ; if (!(lIn = fopen(lDialogFile, "r"))) { remove(lDialogFile); return 0 ; } while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL) {} fclose(lIn); wipefile(lDialogFile); remove(lDialogFile); if ( aoBuff[strlen( aoBuff ) -1] == '\n' ) { aoBuff[strlen( aoBuff ) -1] = '\0' ; } /* printf( "aoBuff: %s\n" , aoBuff ) ; */ /* printf( "aoBuff: %s len: %lu \n" , aoBuff , strlen(aoBuff) ) ; */ lResult = strncmp( aoBuff , "1" , 1) ? 0 : 1 ; /* printf( "lResult: %d \n" , lResult ) ; */ if ( ! lResult ) { return NULL ; } /* printf( "aoBuff+1: %s\n" , aoBuff+1 ) ; */ return aoBuff+3 ; } static char const * saveFileDialogWinConsole( char * const aoBuff , char const * const aTitle , /* NULL or "" */ char const * const aDefaultPathAndFile ) /* NULL or "" */ { char lDialogString[MAX_PATH_OR_CMD]; char lPathAndFile[MAX_PATH_OR_CMD] = ""; FILE * lIn; strcpy( lDialogString , "dialog " ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "--title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } strcat(lDialogString, "--backtitle \"") ; strcat(lDialogString, "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ; strcat(lDialogString, "\" ") ; strcat( lDialogString , "--fselect \"" ) ; if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) { /* dialog.exe uses unix separators even on windows */ strcpy(lPathAndFile, aDefaultPathAndFile); replaceChr( lPathAndFile , '\\' , '/' ) ; } /* dialog.exe needs at least one separator */ if ( ! strchr(lPathAndFile, '/') ) { strcat(lDialogString, "./") ; } strcat(lDialogString, lPathAndFile) ; strcat(lDialogString, "\" 0 60 2>"); strcpy(lPathAndFile, getenv("USERPROFILE")); strcat(lPathAndFile, "\\AppData\\Local\\Temp\\tinyfd.txt"); strcat(lDialogString, lPathAndFile); /* printf( "lDialogString: %s\n" , lDialogString ) ; */ system( lDialogString ) ; if (!(lIn = fopen(lPathAndFile, "r"))) { remove(lPathAndFile); return NULL; } while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL) {} fclose(lIn); remove(lPathAndFile); replaceChr( aoBuff , '/' , '\\' ) ; /* printf( "aoBuff: %s\n" , aoBuff ) ; */ getLastName(lDialogString,aoBuff); if ( ! strlen(lDialogString) ) { return NULL; } return aoBuff; } static char const * openFileDialogWinConsole( char * const aoBuff , char const * const aTitle , /* NULL or "" */ char const * const aDefaultPathAndFile , /* NULL or "" */ int const aAllowMultipleSelects ) /* 0 or 1 */ { char lFilterPatterns[MAX_PATH_OR_CMD] = ""; char lDialogString[MAX_PATH_OR_CMD] ; FILE * lIn; strcpy( lDialogString , "dialog " ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "--title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } strcat(lDialogString, "--backtitle \"") ; strcat(lDialogString, "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ; strcat(lDialogString, "\" ") ; strcat( lDialogString , "--fselect \"" ) ; if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) { /* dialog.exe uses unix separators even on windows */ strcpy(lFilterPatterns, aDefaultPathAndFile); replaceChr( lFilterPatterns , '\\' , '/' ) ; } /* dialog.exe needs at least one separator */ if ( ! strchr(lFilterPatterns, '/') ) { strcat(lDialogString, "./") ; } strcat(lDialogString, lFilterPatterns) ; strcat(lDialogString, "\" 0 60 2>"); strcpy(lFilterPatterns, getenv("USERPROFILE")); strcat(lFilterPatterns, "\\AppData\\Local\\Temp\\tinyfd.txt"); strcat(lDialogString, lFilterPatterns); /* printf( "lDialogString: %s\n" , lDialogString ) ; */ system( lDialogString ) ; if (!(lIn = fopen(lFilterPatterns, "r"))) { remove(lFilterPatterns); return NULL; } while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL) {} fclose(lIn); remove(lFilterPatterns); replaceChr( aoBuff , '/' , '\\' ) ; /* printf( "aoBuff: %s\n" , aoBuff ) ; */ return aoBuff; } static char const * selectFolderDialogWinConsole( char * const aoBuff , char const * const aTitle , /* NULL or "" */ char const * const aDefaultPath ) /* NULL or "" */ { char lDialogString [MAX_PATH_OR_CMD] ; char lString [MAX_PATH_OR_CMD] ; FILE * lIn ; strcpy( lDialogString , "dialog " ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "--title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } strcat(lDialogString, "--backtitle \"") ; strcat(lDialogString, "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ; strcat(lDialogString, "\" ") ; strcat( lDialogString , "--dselect \"" ) ; if ( aDefaultPath && strlen(aDefaultPath) ) { /* dialog.exe uses unix separators even on windows */ strcpy(lString, aDefaultPath) ; ensureFinalSlash(lString); replaceChr( lString , '\\' , '/' ) ; strcat(lDialogString, lString) ; } else { /* dialog.exe needs at least one separator */ strcat(lDialogString, "./") ; } strcat(lDialogString, "\" 0 60 2>"); strcpy(lString, getenv("USERPROFILE")); strcat(lString, "\\AppData\\Local\\Temp\\tinyfd.txt"); strcat(lDialogString, lString); /* printf( "lDialogString: %s\n" , lDialogString ) ; */ system( lDialogString ) ; if (!(lIn = fopen(lString, "r"))) { remove(lString); return NULL; } while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL) {} fclose(lIn); remove(lString); replaceChr( aoBuff , '/' , '\\' ) ; /* printf( "aoBuff: %s\n" , aoBuff ) ; */ return aoBuff; } int tinyfd_messageBox( char const * const aTitle , /* NULL or "" */ char const * const aMessage , /* NULL or "" may contain \n and \t */ char const * const aDialogType , /* "ok" "okcancel" "yesno" "yesnocancel" */ char const * const aIconType , /* "info" "warning" "error" "question" */ int const aDefaultButton ) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */ { char lChar ; #ifndef TINYFD_NOLIB if ((!tinyfd_forceConsole || !(GetConsoleWindow() || dialogPresent())) && (!getenv("SSH_CLIENT") || getenv("DISPLAY"))) { if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "windows"); return 1; } if (tinyfd_winUtf8) { return messageBoxWinGui8( aTitle, aMessage, aDialogType, aIconType, aDefaultButton); } else { return messageBoxWinGuiA( aTitle, aMessage, aDialogType, aIconType, aDefaultButton); } } else #endif /* TINYFD_NOLIB */ if ( dialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return 0;} return messageBoxWinConsole( aTitle,aMessage,aDialogType,aIconType,aDefaultButton); } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return 0;} if (!gWarningDisplayed && !tinyfd_forceConsole ) { gWarningDisplayed = 1; printf("\n\n%s\n", gTitle); printf("%s\n\n", tinyfd_needs); } if ( aTitle && strlen(aTitle) ) { printf("\n%s\n\n", aTitle); } if ( aDialogType && !strcmp("yesno",aDialogType) ) { do { if ( aMessage && strlen(aMessage) ) { printf("%s\n",aMessage); } printf("y/n: "); lChar = (char) tolower( _getch() ) ; printf("\n\n"); } while ( lChar != 'y' && lChar != 'n' ) ; return lChar == 'y' ? 1 : 0 ; } else if ( aDialogType && !strcmp("okcancel",aDialogType) ) { do { if ( aMessage && strlen(aMessage) ) { printf("%s\n",aMessage); } printf("[O]kay/[C]ancel: "); lChar = (char) tolower( _getch() ) ; printf("\n\n"); } while ( lChar != 'o' && lChar != 'c' ) ; return lChar == 'o' ? 1 : 0 ; } else if (aDialogType && !strcmp("yesnocancel", aDialogType)) { do { if (aMessage && strlen(aMessage)) { printf("%s\n", aMessage); } printf("[Y]es/[N]o/[C]ancel: "); lChar = (char)tolower(_getch()); printf("\n\n"); } while (lChar != 'y' && lChar != 'n' && lChar != 'c'); return (lChar == 'y') ? 1 : (lChar == 'n') ? 2 : 0 ; } else { if ( aMessage && strlen(aMessage) ) { printf("%s\n\n",aMessage); } printf("press enter to continue "); lChar = (char) _getch() ; printf("\n\n"); return 1 ; } } } /* return has only meaning for tinyfd_query */ int tinyfd_notifyPopup( char const * const aTitle , /* NULL or "" */ char const * const aMessage , /* NULL or "" may contain \n \t */ char const * const aIconType ) /* "info" "warning" "error" */ { #ifndef TINYFD_NOLIB if ((!tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent())) && ( !getenv("SSH_CLIENT") || getenv("DISPLAY") ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return 1;} return notifyWinGui(aTitle, aMessage, aIconType); } else #endif /* TINYFD_NOLIB */ { return tinyfd_messageBox(aTitle, aMessage, "ok" , aIconType, 0); } } /* returns NULL on cancel */ char const * tinyfd_inputBox( char const * const aTitle , /* NULL or "" */ char const * const aMessage , /* NULL or "" may NOT contain \n nor \t */ char const * const aDefaultInput ) /* "" , if NULL it's a passwordBox */ { static char lBuff [MAX_PATH_OR_CMD] ; char * lEOF; #ifndef TINYFD_NOLIB DWORD mode = 0; HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); if ((!tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent())) && ( !getenv("SSH_CLIENT") || getenv("DISPLAY") ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char const *)1;} lBuff[0]='\0'; return inputBoxWinGui(lBuff, aTitle, aMessage, aDefaultInput); } else #endif /* TINYFD_NOLIB */ if ( dialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} lBuff[0]='\0'; return inputBoxWinConsole(lBuff,aTitle,aMessage,aDefaultInput); } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char const *)0;} lBuff[0]='\0'; if (!gWarningDisplayed && !tinyfd_forceConsole) { gWarningDisplayed = 1 ; printf("\n\n%s\n", gTitle); printf("%s\n\n", tinyfd_needs); } if ( aTitle && strlen(aTitle) ) { printf("\n%s\n\n", aTitle); } if ( aMessage && strlen(aMessage) ) { printf("%s\n",aMessage); } printf("(ctrl-Z + enter to cancel): "); #ifndef TINYFD_NOLIB if ( ! aDefaultInput ) { GetConsoleMode(hStdin,&mode); SetConsoleMode(hStdin,mode & (~ENABLE_ECHO_INPUT) ); } #endif /* TINYFD_NOLIB */ lEOF = fgets(lBuff, MAX_PATH_OR_CMD, stdin); if ( ! lEOF ) { return NULL; } #ifndef TINYFD_NOLIB if ( ! aDefaultInput ) { SetConsoleMode(hStdin,mode); printf("\n"); } #endif /* TINYFD_NOLIB */ printf("\n"); if ( strchr(lBuff,27) ) { return NULL ; } if ( lBuff[strlen( lBuff ) -1] == '\n' ) { lBuff[strlen( lBuff ) -1] = '\0' ; } return lBuff ; } } char const * tinyfd_saveFileDialog( char const * const aTitle , /* NULL or "" */ char const * const aDefaultPathAndFile , /* NULL or "" */ int const aNumOfFilterPatterns , /* 0 */ char const * const * const aFilterPatterns , /* NULL or {"*.jpg","*.png"} */ char const * const aSingleFilterDescription ) /* NULL or "image files" */ { static char lBuff [MAX_PATH_OR_CMD] ; char lString[MAX_PATH_OR_CMD] ; char const * p ; lBuff[0]='\0'; #ifndef TINYFD_NOLIB if ( ( !tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent() ) ) && ( !getenv("SSH_CLIENT") || getenv("DISPLAY") ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char const *)1;} if (tinyfd_winUtf8) { p = saveFileDialogWinGui8(lBuff, aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription); } else { p = saveFileDialogWinGuiA(lBuff, aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription); } } else #endif /* TINYFD_NOLIB */ if ( dialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} p = saveFileDialogWinConsole(lBuff,aTitle,aDefaultPathAndFile); } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char const *)0;} p = tinyfd_inputBox(aTitle, "Save file",""); } if ( ! p || ! strlen( p ) ) { return NULL; } getPathWithoutFinalSlash( lString , p ) ; if ( strlen( lString ) && ! dirExists( lString ) ) { return NULL ; } getLastName(lString,p); if ( ! filenameValid(lString) ) { return NULL; } return p ; } /* in case of multiple files, the separator is | */ char const * tinyfd_openFileDialog( char const * const aTitle , /* NULL or "" */ char const * const aDefaultPathAndFile , /* NULL or "" */ int const aNumOfFilterPatterns , /* 0 */ char const * const * const aFilterPatterns , /* NULL or {"*.jpg","*.png"} */ char const * const aSingleFilterDescription , /* NULL or "image files" */ int const aAllowMultipleSelects ) /* 0 or 1 */ { static char lBuff[MAX_MULTIPLE_FILES*MAX_PATH_OR_CMD]; char const * p ; #ifndef TINYFD_NOLIB if ( ( !tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent() ) ) && ( !getenv("SSH_CLIENT") || getenv("DISPLAY") ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char const *)1;} if (tinyfd_winUtf8) { p = openFileDialogWinGui8(lBuff, aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects); } else { p = openFileDialogWinGuiA(lBuff, aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects); } } else #endif /* TINYFD_NOLIB */ if ( dialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} p = openFileDialogWinConsole(lBuff, aTitle,aDefaultPathAndFile,aAllowMultipleSelects); } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char const *)0;} p = tinyfd_inputBox(aTitle, "Open file",""); } if ( ! p || ! strlen( p ) ) { return NULL; } if ( aAllowMultipleSelects && strchr(p, '|') ) { p = ensureFilesExist( lBuff , p ) ; } else if ( ! fileExists(p) ) { return NULL ; } /* printf( "lBuff3: %s\n" , p ) ; */ return p ; } char const * tinyfd_selectFolderDialog( char const * const aTitle , /* NULL or "" */ char const * const aDefaultPath ) /* NULL or "" */ { static char lBuff [MAX_PATH_OR_CMD] ; char const * p ; #ifndef TINYFD_NOLIB if ( ( !tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent() ) ) && ( !getenv("SSH_CLIENT") || getenv("DISPLAY") ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char const *)1;} if (tinyfd_winUtf8) { #ifndef TINYFD_NOSELECTFOLDERWIN p = selectFolderDialogWinGui8(lBuff, aTitle, aDefaultPath); } else { p = selectFolderDialogWinGuiA(lBuff, aTitle, aDefaultPath); #endif /*TINYFD_NOSELECTFOLDERWIN*/ } } else #endif /* TINYFD_NOLIB */ if ( dialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} p = selectFolderDialogWinConsole(lBuff,aTitle,aDefaultPath); } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char const *)0;} p = tinyfd_inputBox(aTitle, "Select folder",""); } if ( ! p || ! strlen( p ) || ! dirExists( p ) ) { return NULL ; } return p ; } /* returns the hexcolor as a string "#FF0000" */ /* aoResultRGB also contains the result */ /* aDefaultRGB is used only if aDefaultHexRGB is NULL */ /* aDefaultRGB and aoResultRGB can be the same array */ char const * tinyfd_colorChooser( char const * const aTitle, /* NULL or "" */ char const * const aDefaultHexRGB, /* NULL or "#FF0000"*/ unsigned char const aDefaultRGB[3], /* { 0 , 255 , 255 } */ unsigned char aoResultRGB[3]) /* { 0 , 0 , 0 } */ { char lDefaultHexRGB[8]; char * lpDefaultHexRGB; int i; char const * p ; #ifndef TINYFD_NOLIB if ( (!tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent()) ) && (!getenv("SSH_CLIENT") || getenv("DISPLAY")) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char const *)1;} if (tinyfd_winUtf8) { return colorChooserWinGui8( aTitle, aDefaultHexRGB, aDefaultRGB, aoResultRGB); } else { return colorChooserWinGuiA( aTitle, aDefaultHexRGB, aDefaultRGB, aoResultRGB); } } else #endif /* TINYFD_NOLIB */ if ( aDefaultHexRGB ) { lpDefaultHexRGB = (char *) aDefaultHexRGB ; } else { RGB2Hex( aDefaultRGB , lDefaultHexRGB ) ; lpDefaultHexRGB = (char *) lDefaultHexRGB ; } p = tinyfd_inputBox(aTitle, "Enter hex rgb color (i.e. #f5ca20)",lpDefaultHexRGB); if (aTitle&&!strcmp(aTitle,"tinyfd_query")) return p; if ( !p || (strlen(p) != 7) || (p[0] != '#') ) { return NULL ; } for ( i = 1 ; i < 7 ; i ++ ) { if ( ! isxdigit( p[i] ) ) { return NULL ; } } Hex2RGB(p,aoResultRGB); return p ; } #else /* unix */ static char gPython2Name[16]; static char gPython3Name[16]; static char gPythonName[16]; static int isDarwin(void) { static int lsIsDarwin = -1 ; struct utsname lUtsname ; if ( lsIsDarwin < 0 ) { lsIsDarwin = !uname(&lUtsname) && !strcmp(lUtsname.sysname,"Darwin") ; } return lsIsDarwin ; } static int dirExists( char const * const aDirPath ) { DIR * lDir ; if ( ! aDirPath || ! strlen( aDirPath ) ) return 0 ; lDir = opendir( aDirPath ) ; if ( ! lDir ) { return 0 ; } closedir( lDir ) ; return 1 ; } static int detectPresence( char const * const aExecutable ) { char lBuff [MAX_PATH_OR_CMD] ; char lTestedString [MAX_PATH_OR_CMD] = "which " ; FILE * lIn ; strcat( lTestedString , aExecutable ) ; strcat( lTestedString, " 2>/dev/null "); lIn = popen( lTestedString , "r" ) ; if ( ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) && ( ! strchr( lBuff , ':' ) ) && ( strncmp(lBuff, "no ", 3) ) ) { /* present */ pclose( lIn ) ; if (tinyfd_verbose) printf("detectPresence %s %d\n", aExecutable, 1); return 1 ; } else { pclose( lIn ) ; if (tinyfd_verbose) printf("detectPresence %s %d\n", aExecutable, 0); return 0 ; } } static char const * getVersion( char const * const aExecutable ) /*version must be first numeral*/ { static char lBuff [MAX_PATH_OR_CMD] ; char lTestedString [MAX_PATH_OR_CMD] ; FILE * lIn ; char * lTmp ; strcpy( lTestedString , aExecutable ) ; strcat( lTestedString , " --version" ) ; lIn = popen( lTestedString , "r" ) ; lTmp = fgets( lBuff , sizeof( lBuff ) , lIn ) ; pclose( lIn ) ; lTmp += strcspn(lTmp,"0123456789"); /* printf("lTmp:%s\n", lTmp); */ return lTmp ; } static int * const getMajorMinorPatch( char const * const aExecutable ) { static int lArray [3] ; char * lTmp ; lTmp = (char *) getVersion(aExecutable); lArray[0] = atoi( strtok(lTmp," ,.-") ) ; /* printf("lArray0 %d\n", lArray[0]); */ lArray[1] = atoi( strtok(0," ,.-") ) ; /* printf("lArray1 %d\n", lArray[1]); */ lArray[2] = atoi( strtok(0," ,.-") ) ; /* printf("lArray2 %d\n", lArray[2]); */ if ( !lArray[0] && !lArray[1] && !lArray[2] ) return NULL; return lArray ; } static int tryCommand( char const * const aCommand ) { char lBuff [MAX_PATH_OR_CMD] ; FILE * lIn ; lIn = popen( aCommand , "r" ) ; if ( fgets( lBuff , sizeof( lBuff ) , lIn ) == NULL ) { /* present */ pclose( lIn ) ; return 1 ; } else { pclose( lIn ) ; return 0 ; } } static int isTerminalRunning(void) { static int lIsTerminalRunning = -1 ; if ( lIsTerminalRunning < 0 ) { lIsTerminalRunning = isatty(1); if (tinyfd_verbose) printf("isTerminalRunning %d\n", lIsTerminalRunning ); } return lIsTerminalRunning; } static char const * dialogNameOnly(void) { static char lDialogName[128] = "*" ; if ( lDialogName[0] == '*' ) { if ( isDarwin() && * strcpy(lDialogName , "/opt/local/bin/dialog" ) && detectPresence( lDialogName ) ) {} else if ( * strcpy(lDialogName , "dialog" ) && detectPresence( lDialogName ) ) {} else { strcpy(lDialogName , "" ); } } return lDialogName ; } int isDialogVersionBetter09b(void) { char const * lDialogName ; char * lVersion ; int lMajor ; int lMinor ; int lDate ; int lResult ; char * lMinorP ; char * lLetter ; char lBuff[128] ; /*char lTest[128] = " 0.9b-20031126" ;*/ lDialogName = dialogNameOnly() ; if ( ! strlen(lDialogName) || !(lVersion = (char *) getVersion(lDialogName)) ) return 0 ; /*lVersion = lTest ;*/ /*printf("lVersion %s\n", lVersion);*/ strcpy(lBuff,lVersion); lMajor = atoi( strtok(lVersion," ,.-") ) ; /*printf("lMajor %d\n", lMajor);*/ lMinorP = strtok(0," ,.-abcdefghijklmnopqrstuvxyz"); lMinor = atoi( lMinorP ) ; /*printf("lMinor %d\n", lMinor );*/ lDate = atoi( strtok(0," ,.-") ) ; if (lDate<0) lDate = - lDate; /*printf("lDate %d\n", lDate);*/ lLetter = lMinorP + strlen(lMinorP) ; strcpy(lVersion,lBuff); strtok(lLetter," ,.-"); /*printf("lLetter %s\n", lLetter);*/ lResult = (lMajor > 0) || ( ( lMinor == 9 ) && (*lLetter == 'b') && (lDate >= 20031126) ); /*printf("lResult %d\n", lResult);*/ return lResult; } static int whiptailPresentOnly(void) { static int lWhiptailPresent = -1 ; if ( lWhiptailPresent < 0 ) { lWhiptailPresent = detectPresence( "whiptail" ) ; } return lWhiptailPresent ; } static char const * terminalName(void) { static char lTerminalName[128] = "*" ; char lShellName[64] = "*" ; int * lArray; if ( lTerminalName[0] == '*' ) { if ( detectPresence( "bash" ) ) { strcpy(lShellName , "bash -c " ) ; /*good for basic input*/ } else if ( strlen(dialogNameOnly()) || whiptailPresentOnly() ) { strcpy(lShellName , "sh -c " ) ; /*good enough for dialog & whiptail*/ } else { strcpy(lTerminalName , "" ) ; return NULL ; } if ( isDarwin() ) { if ( * strcpy(lTerminalName , "/opt/X11/bin/xterm" ) && detectPresence( lTerminalName ) ) { strcat(lTerminalName , " -fa 'DejaVu Sans Mono' -fs 10 -title tinyfiledialogs -e " ) ; strcat(lTerminalName , lShellName ) ; } else { strcpy(lTerminalName , "" ) ; } } else if ( * strcpy(lTerminalName,"xterm") /*good (small without parameters)*/ && detectPresence(lTerminalName) ) { strcat(lTerminalName , " -fa 'DejaVu Sans Mono' -fs 10 -title tinyfiledialogs -e " ) ; strcat(lTerminalName , lShellName ) ; } else if ( * strcpy(lTerminalName,"terminator") /*good*/ && detectPresence(lTerminalName) ) { strcat(lTerminalName , " -x " ) ; strcat(lTerminalName , lShellName ) ; } else if ( * strcpy(lTerminalName,"lxterminal") /*good*/ && detectPresence(lTerminalName) ) { strcat(lTerminalName , " -e " ) ; strcat(lTerminalName , lShellName ) ; } else if ( * strcpy(lTerminalName,"konsole") /*good*/ && detectPresence(lTerminalName) ) { strcat(lTerminalName , " -e " ) ; strcat(lTerminalName , lShellName ) ; } else if ( * strcpy(lTerminalName,"kterm") /*good*/ && detectPresence(lTerminalName) ) { strcat(lTerminalName , " -e " ) ; strcat(lTerminalName , lShellName ) ; } else if ( * strcpy(lTerminalName,"tilix") /*good*/ && detectPresence(lTerminalName) ) { strcat(lTerminalName , " -e " ) ; strcat(lTerminalName , lShellName ) ; } else if ( * strcpy(lTerminalName,"xfce4-terminal") /*good*/ && detectPresence(lTerminalName) ) { strcat(lTerminalName , " -x " ) ; strcat(lTerminalName , lShellName ) ; } else if ( * strcpy(lTerminalName,"mate-terminal") /*good*/ && detectPresence(lTerminalName) ) { strcat(lTerminalName , " -x " ) ; strcat(lTerminalName , lShellName ) ; } else if ( * strcpy(lTerminalName,"Eterm") /*good*/ && detectPresence(lTerminalName) ) { strcat(lTerminalName , " -e " ) ; strcat(lTerminalName , lShellName ) ; } else if ( * strcpy(lTerminalName,"evilvte") /*good*/ && detectPresence(lTerminalName) ) { strcat(lTerminalName , " -e " ) ; strcat(lTerminalName , lShellName ) ; } else if ( * strcpy(lTerminalName,"pterm") /*good (only letters)*/ && detectPresence(lTerminalName) ) { strcat(lTerminalName , " -e " ) ; strcat(lTerminalName , lShellName ) ; } else if ( * strcpy(lTerminalName,"gnome-terminal") && detectPresence(lTerminalName) && (lArray = getMajorMinorPatch(lTerminalName)) && ((lArray[0]<3) || (lArray[0]==3 && lArray[1]<=6)) ) { strcat(lTerminalName , " --disable-factory -x " ) ; strcat(lTerminalName , lShellName ) ; } else { strcpy(lTerminalName , "" ) ; } /* bad: koi rxterm guake tilda vala-terminal qterminal aterm Terminal terminology sakura lilyterm weston-terminal roxterm termit xvt rxvt mrxvt urxvt */ } if ( strlen(lTerminalName) ) { return lTerminalName ; } else { return NULL ; } } static char const * dialogName(void) { char const * lDialogName ; lDialogName = dialogNameOnly( ) ; if ( strlen(lDialogName) && ( isTerminalRunning() || terminalName() ) ) { return lDialogName ; } else { return NULL ; } } static int whiptailPresent(void) { int lWhiptailPresent ; lWhiptailPresent = whiptailPresentOnly( ) ; if ( lWhiptailPresent && ( isTerminalRunning() || terminalName() ) ) { return lWhiptailPresent ; } else { return 0 ; } } static int graphicMode(void) { return !( tinyfd_forceConsole && (isTerminalRunning() || terminalName()) ) && ( getenv("DISPLAY") || (isDarwin() && (!getenv("SSH_TTY") || getenv("DISPLAY") ) ) ) ; } static int pactlPresent(void) { static int lPactlPresent = -1 ; if ( lPactlPresent < 0 ) { lPactlPresent = detectPresence("pactl") ; } return lPactlPresent ; } static int speakertestPresent(void) { static int lSpeakertestPresent = -1 ; if ( lSpeakertestPresent < 0 ) { lSpeakertestPresent = detectPresence("speaker-test") ; } return lSpeakertestPresent ; } static int beepexePresent(void) { static int lBeepexePresent = -1 ; if ( lBeepexePresent < 0 ) { lBeepexePresent = detectPresence("beep.exe") ; } return lBeepexePresent ; } static int xmessagePresent(void) { static int lXmessagePresent = -1 ; if ( lXmessagePresent < 0 ) { lXmessagePresent = detectPresence("xmessage");/*if not tty,not on osxpath*/ } return lXmessagePresent && graphicMode( ) ; } static int gxmessagePresent(void) { static int lGxmessagePresent = -1 ; if ( lGxmessagePresent < 0 ) { lGxmessagePresent = detectPresence("gxmessage") ; } return lGxmessagePresent && graphicMode( ) ; } static int gmessagePresent(void) { static int lGmessagePresent = -1 ; if ( lGmessagePresent < 0 ) { lGmessagePresent = detectPresence("gmessage") ; } return lGmessagePresent && graphicMode( ) ; } static int notifysendPresent(void) { static int lNotifysendPresent = -1 ; if ( lNotifysendPresent < 0 ) { lNotifysendPresent = detectPresence("notify-send") ; } return lNotifysendPresent && graphicMode( ) ; } static int perlPresent(void) { static int lPerlPresent = -1 ; char lBuff [MAX_PATH_OR_CMD] ; FILE * lIn ; if ( lPerlPresent < 0 ) { lPerlPresent = detectPresence("perl") ; if ( lPerlPresent ) { lIn = popen( "perl -MNet::DBus -e \"Net::DBus->session->get_service('org.freedesktop.Notifications')\" 2>&1" , "r" ) ; if ( fgets( lBuff , sizeof( lBuff ) , lIn ) == NULL ) { lPerlPresent = 2 ; } pclose( lIn ) ; if (tinyfd_verbose) printf("perl-dbus %d\n", lPerlPresent); } } return graphicMode() ? lPerlPresent : 0 ; } static int afplayPresent(void) { static int lAfplayPresent = -1 ; char lBuff [MAX_PATH_OR_CMD] ; FILE * lIn ; if ( lAfplayPresent < 0 ) { lAfplayPresent = detectPresence("afplay") ; if ( lAfplayPresent ) { lIn = popen( "test -e /System/Library/Sounds/Ping.aiff || echo Ping" , "r" ) ; if ( fgets( lBuff , sizeof( lBuff ) , lIn ) == NULL ) { lAfplayPresent = 2 ; } pclose( lIn ) ; if (tinyfd_verbose) printf("afplay %d\n", lAfplayPresent); } } return graphicMode() ? lAfplayPresent : 0 ; } static int xdialogPresent(void) { static int lXdialogPresent = -1 ; if ( lXdialogPresent < 0 ) { lXdialogPresent = detectPresence("Xdialog") ; } return lXdialogPresent && graphicMode( ) ; } static int gdialogPresent(void) { static int lGdialoglPresent = -1 ; if ( lGdialoglPresent < 0 ) { lGdialoglPresent = detectPresence( "gdialog" ) ; } return lGdialoglPresent && graphicMode( ) ; } static int osascriptPresent(void) { static int lOsascriptPresent = -1 ; if ( lOsascriptPresent < 0 ) { gWarningDisplayed |= !!getenv("SSH_TTY"); lOsascriptPresent = detectPresence( "osascript" ) ; } return lOsascriptPresent && graphicMode() && !getenv("SSH_TTY") ; } static int qarmaPresent(void) { static int lQarmaPresent = -1 ; if ( lQarmaPresent < 0 ) { lQarmaPresent = detectPresence("qarma") ; } return lQarmaPresent && graphicMode( ) ; } static int matedialogPresent(void) { static int lMatedialogPresent = -1 ; if ( lMatedialogPresent < 0 ) { lMatedialogPresent = detectPresence("matedialog") ; } return lMatedialogPresent && graphicMode( ) ; } static int shellementaryPresent(void) { static int lShellementaryPresent = -1 ; if ( lShellementaryPresent < 0 ) { lShellementaryPresent = 0 ; /*detectPresence("shellementary"); shellementary is not ready yet */ } return lShellementaryPresent && graphicMode( ) ; } static int zenityPresent(void) { static int lZenityPresent = -1 ; if ( lZenityPresent < 0 ) { lZenityPresent = detectPresence("zenity") ; } return lZenityPresent && graphicMode( ) ; } static int zenity3Present(void) { static int lZenity3Present = -1 ; char lBuff [MAX_PATH_OR_CMD] ; FILE * lIn ; int lIntTmp ; if ( lZenity3Present < 0 ) { lZenity3Present = 0 ; if ( zenityPresent() ) { lIn = popen( "zenity --version" , "r" ) ; if ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) { if ( atoi(lBuff) >= 3 ) { lZenity3Present = 3 ; lIntTmp = atoi(strtok(lBuff,".")+2 ) ; if ( lIntTmp >= 18 ) { lZenity3Present = 5 ; } else if ( lIntTmp >= 10 ) { lZenity3Present = 4 ; } } else if ( ( atoi(lBuff) == 2 ) && ( atoi(strtok(lBuff,".")+2 ) >= 32 ) ) { lZenity3Present = 2 ; } if (tinyfd_verbose) printf("zenity %d\n", lZenity3Present); } pclose( lIn ) ; } } return graphicMode() ? lZenity3Present : 0 ; } static int kdialogPresent(void) { static int lKdialogPresent = -1 ; char lBuff [MAX_PATH_OR_CMD] ; FILE * lIn ; char * lDesktop; if ( lKdialogPresent < 0 ) { if ( zenityPresent() ) { lDesktop = getenv("XDG_SESSION_DESKTOP"); if ( !lDesktop || ( strcmp(lDesktop, "KDE") && strcmp(lDesktop, "lxqt") ) ) { lKdialogPresent = 0 ; return lKdialogPresent ; } } lKdialogPresent = detectPresence("kdialog") ; if ( lKdialogPresent && !getenv("SSH_TTY") ) { lIn = popen( "kdialog --attach 2>&1" , "r" ) ; if ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) { if ( ! strstr( "Unknown" , lBuff ) ) { lKdialogPresent = 2 ; if (tinyfd_verbose) printf("kdialog-attach %d\n", lKdialogPresent); } } pclose( lIn ) ; if (lKdialogPresent == 2) { lKdialogPresent = 1 ; lIn = popen( "kdialog --passivepopup 2>&1" , "r" ) ; if ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) { if ( ! strstr( "Unknown" , lBuff ) ) { lKdialogPresent = 2 ; if (tinyfd_verbose) printf("kdialog-popup %d\n", lKdialogPresent); } } pclose( lIn ) ; } } } return graphicMode() ? lKdialogPresent : 0 ; } static int osx9orBetter(void) { static int lOsx9orBetter = -1 ; char lBuff [MAX_PATH_OR_CMD] ; FILE * lIn ; int V,v; if ( lOsx9orBetter < 0 ) { lOsx9orBetter = 0 ; lIn = popen( "osascript -e 'set osver to system version of (system info)'" , "r" ) ; if ( ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) && ( 2 == sscanf(lBuff, "%d.%d", &V, &v) ) ) { V = V * 100 + v; if ( V >= 1009 ) { lOsx9orBetter = 1 ; } } pclose( lIn ) ; if (tinyfd_verbose) printf("Osx10 = %d, %d = %s\n", lOsx9orBetter, V, lBuff) ; } return lOsx9orBetter ; } static int python2Present(void) { static int lPython2Present = -1 ; int i; if ( lPython2Present < 0 ) { lPython2Present = 0 ; strcpy(gPython2Name , "python2" ) ; if ( detectPresence(gPython2Name) ) lPython2Present = 1; else { for ( i = 9 ; i >= 0 ; i -- ) { sprintf( gPython2Name , "python2.%d" , i ) ; if ( detectPresence(gPython2Name) ) { lPython2Present = 1; break; } } /*if ( ! lPython2Present ) { strcpy(gPython2Name , "python" ) ; if ( detectPresence(gPython2Name) ) lPython2Present = 1; }*/ } if (tinyfd_verbose) printf("lPython2Present %d\n", lPython2Present) ; if (tinyfd_verbose) printf("gPython2Name %s\n", gPython2Name) ; } return lPython2Present ; } static int python3Present(void) { static int lPython3Present = -1 ; int i; if ( lPython3Present < 0 ) { lPython3Present = 0 ; strcpy(gPython3Name , "python3" ) ; if ( detectPresence(gPython3Name) ) lPython3Present = 1; else { for ( i = 9 ; i >= 0 ; i -- ) { sprintf( gPython3Name , "python3.%d" , i ) ; if ( detectPresence(gPython3Name) ) { lPython3Present = 1; break; } } /*if ( ! lPython3Present ) { strcpy(gPython3Name , "python" ) ; if ( detectPresence(gPython3Name) ) lPython3Present = 1; }*/ } if (tinyfd_verbose) printf("lPython3Present %d\n", lPython3Present) ; if (tinyfd_verbose) printf("gPython3Name %s\n", gPython3Name) ; } return lPython3Present ; } static int tkinter2Present(void) { static int lTkinter2Present = -1 ; char lPythonCommand[256]; char lPythonParams[128] = "-S -c \"try:\n\timport Tkinter;\nexcept:\n\tprint 0;\""; if ( lTkinter2Present < 0 ) { lTkinter2Present = 0 ; if ( python2Present() ) { sprintf( lPythonCommand , "%s %s" , gPython2Name , lPythonParams ) ; lTkinter2Present = tryCommand(lPythonCommand) ; } if (tinyfd_verbose) printf("lTkinter2Present %d\n", lTkinter2Present) ; } return lTkinter2Present && graphicMode() && !(isDarwin() && getenv("SSH_TTY") ); } static int tkinter3Present(void) { static int lTkinter3Present = -1 ; char lPythonCommand[256]; char lPythonParams[128] = "-S -c \"try:\n\timport tkinter;\nexcept:\n\tprint(0);\""; if ( lTkinter3Present < 0 ) { lTkinter3Present = 0 ; if ( python3Present() ) { sprintf( lPythonCommand , "%s %s" , gPython3Name , lPythonParams ) ; lTkinter3Present = tryCommand(lPythonCommand) ; } if (tinyfd_verbose) printf("lTkinter3Present %d\n", lTkinter3Present) ; } return lTkinter3Present && graphicMode() && !(isDarwin() && getenv("SSH_TTY") ); } static int pythonDbusPresent(void) { static int lDbusPresent = -1 ; char lPythonCommand[384]; char lPythonParams[256] = "-c \"try:\n\timport dbus;bus=dbus.SessionBus();\ notif=bus.get_object('org.freedesktop.Notifications','/org/freedesktop/Notifications');\ notify=dbus.Interface(notif,'org.freedesktop.Notifications');\nexcept:\n\tprint(0);\""; if ( lDbusPresent < 0 ) { lDbusPresent = 0 ; if ( python2Present() ) { strcpy(gPythonName , gPython2Name ) ; sprintf( lPythonCommand , "%s %s" , gPythonName , lPythonParams ) ; lDbusPresent = tryCommand(lPythonCommand) ; } if ( ! lDbusPresent && python3Present() ) { strcpy(gPythonName , gPython3Name ) ; sprintf( lPythonCommand , "%s %s" , gPythonName , lPythonParams ) ; lDbusPresent = tryCommand(lPythonCommand) ; } if (tinyfd_verbose) printf("lDbusPresent %d\n", lDbusPresent) ; if (tinyfd_verbose) printf("gPythonName %s\n", gPythonName) ; } return lDbusPresent && graphicMode() && !(isDarwin() && getenv("SSH_TTY") ); } static void sigHandler(int sig) { FILE * lIn ; if ( ( lIn = popen( "pactl unload-module module-sine" , "r" ) ) ) { pclose( lIn ) ; } } void tinyfd_beep(void) { char lDialogString [256] ; FILE * lIn ; if ( osascriptPresent() ) { if ( afplayPresent() >= 2 ) { strcpy( lDialogString , "afplay /System/Library/Sounds/Ping.aiff") ; } else { strcpy( lDialogString , "osascript -e 'tell application \"System Events\" to beep'") ; } } else if ( pactlPresent() ) { signal(SIGINT, sigHandler); /*strcpy( lDialogString , "pactl load-module module-sine frequency=440;sleep .3;pactl unload-module module-sine" ) ;*/ strcpy( lDialogString , "thnum=$(pactl load-module module-sine frequency=440);sleep .3;pactl unload-module $thnum" ) ; } else if ( speakertestPresent() ) { /*strcpy( lDialogString , "timeout -k .3 .3 speaker-test --frequency 440 --test sine > /dev/tty" ) ;*/ strcpy( lDialogString , "( speaker-test -t sine -f 440 > /dev/tty )& pid=$!;sleep .3; kill -9 $pid" ) ; } else if ( beepexePresent() ) { strcpy( lDialogString , "beep.exe 440 300" ) ; } else { strcpy( lDialogString , "printf '\a' > /dev/tty" ) ; } if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ; if ( ( lIn = popen( lDialogString , "r" ) ) ) { pclose( lIn ) ; } if ( pactlPresent() ) { signal(SIGINT, SIG_DFL); } } int tinyfd_messageBox( char const * const aTitle , /* NULL or "" */ char const * const aMessage , /* NULL or "" may contain \n and \t */ char const * const aDialogType , /* "ok" "okcancel" "yesno" "yesnocancel" */ char const * const aIconType , /* "info" "warning" "error" "question" */ int const aDefaultButton ) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */ { char lBuff [MAX_PATH_OR_CMD] ; char * lDialogString = NULL ; char * lpDialogString; FILE * lIn ; int lWasGraphicDialog = 0 ; int lWasXterm = 0 ; int lResult ; char lChar ; struct termios infoOri; struct termios info; size_t lTitleLen ; size_t lMessageLen ; lBuff[0]='\0'; lTitleLen = aTitle ? strlen(aTitle) : 0 ; lMessageLen = aMessage ? strlen(aMessage) : 0 ; if ( !aTitle || strcmp(aTitle,"tinyfd_query") ) { lDialogString = (char *) malloc( MAX_PATH_OR_CMD + lTitleLen + lMessageLen ); } if ( osascriptPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return 1;} strcpy( lDialogString , "osascript "); if ( ! osx9orBetter() ) strcat( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'"); strcat( lDialogString , " -e 'try' -e 'set {vButton} to {button returned} of ( display dialog \"") ; if ( aMessage && strlen(aMessage) ) { strcat(lDialogString, aMessage) ; } strcat(lDialogString, "\" ") ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "with title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } strcat(lDialogString, "with icon ") ; if ( aIconType && ! strcmp( "error" , aIconType ) ) { strcat(lDialogString, "stop " ) ; } else if ( aIconType && ! strcmp( "warning" , aIconType ) ) { strcat(lDialogString, "caution " ) ; } else /* question or info */ { strcat(lDialogString, "note " ) ; } if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) ) { if ( ! aDefaultButton ) { strcat( lDialogString ,"default button \"Cancel\" " ) ; } } else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) ) { strcat( lDialogString ,"buttons {\"No\", \"Yes\"} " ) ; if (aDefaultButton) { strcat( lDialogString ,"default button \"Yes\" " ) ; } else { strcat( lDialogString ,"default button \"No\" " ) ; } strcat( lDialogString ,"cancel button \"No\"" ) ; } else if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) ) { strcat( lDialogString ,"buttons {\"No\", \"Yes\", \"Cancel\"} " ) ; switch (aDefaultButton) { case 1: strcat( lDialogString ,"default button \"Yes\" " ) ; break; case 2: strcat( lDialogString ,"default button \"No\" " ) ; break; case 0: strcat( lDialogString ,"default button \"Cancel\" " ) ; break; } strcat( lDialogString ,"cancel button \"Cancel\"" ) ; } else { strcat( lDialogString ,"buttons {\"OK\"} " ) ; strcat( lDialogString ,"default button \"OK\" " ) ; } strcat( lDialogString, ")' ") ; strcat( lDialogString, "-e 'if vButton is \"Yes\" then' -e 'return 1'\ -e 'else if vButton is \"OK\" then' -e 'return 1'\ -e 'else if vButton is \"No\" then' -e 'return 2'\ -e 'else' -e 'return 0' -e 'end if' " ); strcat( lDialogString, "-e 'on error number -128' " ) ; strcat( lDialogString, "-e '0' " ); strcat( lDialogString, "-e 'end try'") ; if ( ! osx9orBetter() ) strcat( lDialogString, " -e 'end tell'") ; } else if ( kdialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return 1;} strcpy( lDialogString , "kdialog" ) ; if ( kdialogPresent() == 2 ) { strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } strcat( lDialogString , " --" ) ; if ( aDialogType && ( ! strcmp( "okcancel" , aDialogType ) || ! strcmp( "yesno" , aDialogType ) || ! strcmp( "yesnocancel" , aDialogType ) ) ) { if ( aIconType && ( ! strcmp( "warning" , aIconType ) || ! strcmp( "error" , aIconType ) ) ) { strcat( lDialogString , "warning" ) ; } if ( ! strcmp( "yesnocancel" , aDialogType ) ) { strcat( lDialogString , "yesnocancel" ) ; } else { strcat( lDialogString , "yesno" ) ; } } else if ( aIconType && ! strcmp( "error" , aIconType ) ) { strcat( lDialogString , "error" ) ; } else if ( aIconType && ! strcmp( "warning" , aIconType ) ) { strcat( lDialogString , "sorry" ) ; } else { strcat( lDialogString , "msgbox" ) ; } strcat( lDialogString , " \"" ) ; if ( aMessage ) { strcat( lDialogString , aMessage ) ; } strcat( lDialogString , "\"" ) ; if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) ) { strcat( lDialogString , " --yes-label Ok --no-label Cancel" ) ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, " --title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\"") ; } if ( ! strcmp( "yesnocancel" , aDialogType ) ) { strcat( lDialogString , "; x=$? ;if [ $x = 0 ] ;then echo 1;elif [ $x = 1 ] ;then echo 2;else echo 0;fi"); } else { strcat( lDialogString , ";if [ $? = 0 ];then echo 1;else echo 0;fi"); } } else if ( zenityPresent() || matedialogPresent() || shellementaryPresent() || qarmaPresent() ) { if ( zenityPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return 1;} strcpy( lDialogString , "szAnswer=$(zenity" ) ; if ( (zenity3Present() >= 4) && !getenv("SSH_TTY") ) { strcat(lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } } else if ( matedialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return 1;} strcpy( lDialogString , "szAnswer=$(matedialog" ) ; } else if ( shellementaryPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return 1;} strcpy( lDialogString , "szAnswer=$(shellementary" ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return 1;} strcpy( lDialogString , "szAnswer=$(qarma" ) ; if ( !getenv("SSH_TTY") ) { strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } } strcat(lDialogString, " --"); if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) ) { strcat( lDialogString , "question --ok-label=Ok --cancel-label=Cancel" ) ; } else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) ) { strcat( lDialogString , "question" ) ; } else if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) ) { strcat( lDialogString , "list --column \"\" --hide-header \"Yes\" \"No\"" ) ; } else if ( aIconType && ! strcmp( "error" , aIconType ) ) { strcat( lDialogString , "error" ) ; } else if ( aIconType && ! strcmp( "warning" , aIconType ) ) { strcat( lDialogString , "warning" ) ; } else { strcat( lDialogString , "info" ) ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, " --title=\"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\"") ; } if ( aMessage && strlen(aMessage) ) { strcat(lDialogString, " --no-wrap --text=\"") ; strcat(lDialogString, aMessage) ; strcat(lDialogString, "\"") ; } if ( (zenity3Present() >= 3) || (!zenityPresent() && (shellementaryPresent() || qarmaPresent()) ) ) { strcat( lDialogString , " --icon-name=dialog-" ) ; if ( aIconType && (! strcmp( "question" , aIconType ) || ! strcmp( "error" , aIconType ) || ! strcmp( "warning" , aIconType ) ) ) { strcat( lDialogString , aIconType ) ; } else { strcat( lDialogString , "information" ) ; } } if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null "); if ( ! strcmp( "yesnocancel" , aDialogType ) ) { strcat( lDialogString , ");if [ $? = 1 ];then echo 0;elif [ $szAnswer = \"No\" ];then echo 2;else echo 1;fi"); } else { strcat( lDialogString , ");if [ $? = 0 ];then echo 1;else echo 0;fi"); } } else if ( !gxmessagePresent() && !gmessagePresent() && !gdialogPresent() && !xdialogPresent() && tkinter2Present() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return 1;} strcpy( lDialogString , gPython2Name ) ; if ( ! isTerminalRunning( ) && isDarwin( ) ) { strcat( lDialogString , " -i" ) ; /* for osx without console */ } strcat( lDialogString , " -S -c \"import Tkinter,tkMessageBox;root=Tkinter.Tk();root.withdraw();"); if ( isDarwin( ) ) { strcat( lDialogString , "import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set \ frontmost of process \\\"Python\\\" to true' ''');"); } strcat( lDialogString ,"res=tkMessageBox." ) ; if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) ) { strcat( lDialogString , "askokcancel(" ) ; if ( aDefaultButton ) { strcat( lDialogString , "default=tkMessageBox.OK," ) ; } else { strcat( lDialogString , "default=tkMessageBox.CANCEL," ) ; } } else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) ) { strcat( lDialogString , "askyesno(" ) ; if ( aDefaultButton ) { strcat( lDialogString , "default=tkMessageBox.YES," ) ; } else { strcat( lDialogString , "default=tkMessageBox.NO," ) ; } } else if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) ) { strcat( lDialogString , "askyesnocancel(" ) ; switch ( aDefaultButton ) { case 1: strcat( lDialogString , "default=tkMessageBox.YES," ); break; case 2: strcat( lDialogString , "default=tkMessageBox.NO," ); break; case 0: strcat( lDialogString , "default=tkMessageBox.CANCEL," ); break; } } else { strcat( lDialogString , "showinfo(" ) ; } strcat( lDialogString , "icon='" ) ; if ( aIconType && (! strcmp( "question" , aIconType ) || ! strcmp( "error" , aIconType ) || ! strcmp( "warning" , aIconType ) ) ) { strcat( lDialogString , aIconType ) ; } else { strcat( lDialogString , "info" ) ; } strcat(lDialogString, "',") ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "title='") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "',") ; } if ( aMessage && strlen(aMessage) ) { strcat(lDialogString, "message='") ; lpDialogString = lDialogString + strlen(lDialogString); replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ; strcat(lDialogString, "'") ; } if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) ) { strcat(lDialogString, ");\n\ if res is None :\n\tprint 0\n\ elif res is False :\n\tprint 2\n\ else :\n\tprint 1\n\"" ) ; } else { strcat(lDialogString, ");\n\ if res is False :\n\tprint 0\n\ else :\n\tprint 1\n\"" ) ; } } else if ( !gxmessagePresent() && !gmessagePresent() && !gdialogPresent() && !xdialogPresent() && tkinter3Present() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return 1;} strcpy( lDialogString , gPython3Name ) ; strcat( lDialogString , " -S -c \"import tkinter;from tkinter import messagebox;root=tkinter.Tk();root.withdraw();"); strcat( lDialogString ,"res=messagebox." ) ; if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) ) { strcat( lDialogString , "askokcancel(" ) ; if ( aDefaultButton ) { strcat( lDialogString , "default=messagebox.OK," ) ; } else { strcat( lDialogString , "default=messagebox.CANCEL," ) ; } } else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) ) { strcat( lDialogString , "askyesno(" ) ; if ( aDefaultButton ) { strcat( lDialogString , "default=messagebox.YES," ) ; } else { strcat( lDialogString , "default=messagebox.NO," ) ; } } else if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) ) { strcat( lDialogString , "askyesnocancel(" ) ; switch ( aDefaultButton ) { case 1: strcat( lDialogString , "default=messagebox.YES," ); break; case 2: strcat( lDialogString , "default=messagebox.NO," ); break; case 0: strcat( lDialogString , "default=messagebox.CANCEL," ); break; } } else { strcat( lDialogString , "showinfo(" ) ; } strcat( lDialogString , "icon='" ) ; if ( aIconType && (! strcmp( "question" , aIconType ) || ! strcmp( "error" , aIconType ) || ! strcmp( "warning" , aIconType ) ) ) { strcat( lDialogString , aIconType ) ; } else { strcat( lDialogString , "info" ) ; } strcat(lDialogString, "',") ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "title='") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "',") ; } if ( aMessage && strlen(aMessage) ) { strcat(lDialogString, "message='") ; lpDialogString = lDialogString + strlen(lDialogString); replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ; strcat(lDialogString, "'") ; } if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) ) { strcat(lDialogString, ");\n\ if res is None :\n\tprint(0)\n\ elif res is False :\n\tprint(2)\n\ else :\n\tprint 1\n\"" ) ; } else { strcat(lDialogString, ");\n\ if res is False :\n\tprint(0)\n\ else :\n\tprint(1)\n\"" ) ; } } else if ( gxmessagePresent() || gmessagePresent() || (!gdialogPresent() && !xdialogPresent() && xmessagePresent()) ) { if ( gxmessagePresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gxmessage");return 1;} strcpy( lDialogString , "gxmessage"); } else if ( gmessagePresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gmessage");return 1;} strcpy( lDialogString , "gmessage"); } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xmessage");return 1;} strcpy( lDialogString , "xmessage"); } if ( aDialogType && ! strcmp("okcancel" , aDialogType) ) { strcat( lDialogString , " -buttons Ok:1,Cancel:0"); switch ( aDefaultButton ) { case 1: strcat( lDialogString , " -default Ok"); break; case 0: strcat( lDialogString , " -default Cancel"); break; } } else if ( aDialogType && ! strcmp("yesno" , aDialogType) ) { strcat( lDialogString , " -buttons Yes:1,No:0"); switch ( aDefaultButton ) { case 1: strcat( lDialogString , " -default Yes"); break; case 0: strcat( lDialogString , " -default No"); break; } } else if ( aDialogType && ! strcmp("yesnocancel" , aDialogType) ) { strcat( lDialogString , " -buttons Yes:1,No:2,Cancel:0"); switch ( aDefaultButton ) { case 1: strcat( lDialogString , " -default Yes"); break; case 2: strcat( lDialogString , " -default No"); break; case 0: strcat( lDialogString , " -default Cancel"); break; } } else { strcat( lDialogString , " -buttons Ok:1"); strcat( lDialogString , " -default Ok"); } strcat( lDialogString , " -center \""); if ( aMessage && strlen(aMessage) ) { strcat( lDialogString , aMessage ) ; } strcat(lDialogString, "\"" ) ; if ( aTitle && strlen(aTitle) ) { strcat( lDialogString , " -title \""); strcat( lDialogString , aTitle ) ; strcat( lDialogString, "\"" ) ; } strcat( lDialogString , " ; echo $? "); } else if ( xdialogPresent() || gdialogPresent() || dialogName() || whiptailPresent() ) { if ( gdialogPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gdialog");return 1;} lWasGraphicDialog = 1 ; strcpy( lDialogString , "(gdialog " ) ; } else if ( xdialogPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return 1;} lWasGraphicDialog = 1 ; strcpy( lDialogString , "(Xdialog " ) ; } else if ( dialogName( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return 0;} if ( isTerminalRunning( ) ) { strcpy( lDialogString , "(dialog " ) ; } else { lWasXterm = 1 ; strcpy( lDialogString , terminalName() ) ; strcat( lDialogString , "'(" ) ; strcat( lDialogString , dialogName() ) ; strcat( lDialogString , " " ) ; } } else if ( isTerminalRunning( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"whiptail");return 0;} strcpy( lDialogString , "(whiptail " ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"whiptail");return 0;} lWasXterm = 1 ; strcpy( lDialogString , terminalName() ) ; strcat( lDialogString , "'(whiptail " ) ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "--title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } if ( !xdialogPresent() && !gdialogPresent() ) { if ( aDialogType && ( !strcmp( "okcancel" , aDialogType ) || !strcmp( "yesno" , aDialogType ) || !strcmp( "yesnocancel" , aDialogType ) ) ) { strcat(lDialogString, "--backtitle \"") ; strcat(lDialogString, "tab: move focus") ; strcat(lDialogString, "\" ") ; } } if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) ) { if ( ! aDefaultButton ) { strcat( lDialogString , "--defaultno " ) ; } strcat( lDialogString , "--yes-label \"Ok\" --no-label \"Cancel\" --yesno " ) ; } else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) ) { if ( ! aDefaultButton ) { strcat( lDialogString , "--defaultno " ) ; } strcat( lDialogString , "--yesno " ) ; } else if (aDialogType && !strcmp("yesnocancel", aDialogType)) { if (!aDefaultButton) { strcat(lDialogString, "--defaultno "); } strcat(lDialogString, "--menu "); } else { strcat( lDialogString , "--msgbox " ) ; } strcat( lDialogString , "\"" ) ; if ( aMessage && strlen(aMessage) ) { strcat(lDialogString, aMessage) ; } strcat(lDialogString, "\" "); if ( lWasGraphicDialog ) { if (aDialogType && !strcmp("yesnocancel", aDialogType)) { strcat(lDialogString,"0 60 0 Yes \"\" No \"\") 2>/tmp/tinyfd.txt;\ if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;\ tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes") ; } else { strcat(lDialogString, "10 60 ) 2>&1;if [ $? = 0 ];then echo 1;else echo 0;fi"); } } else { if (aDialogType && !strcmp("yesnocancel", aDialogType)) { strcat(lDialogString,"0 60 0 Yes \"\" No \"\" >/dev/tty ) 2>/tmp/tinyfd.txt;\ if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;\ tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes") ; if ( lWasXterm ) { strcat(lDialogString," >/tmp/tinyfd0.txt';cat /tmp/tinyfd0.txt"); } else { strcat(lDialogString, "; clear >/dev/tty") ; } } else { strcat(lDialogString, "10 60 >/dev/tty) 2>&1;if [ $? = 0 ];"); if ( lWasXterm ) { strcat( lDialogString , "then\n\techo 1\nelse\n\techo 0\nfi >/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt"); } else { strcat(lDialogString, "then echo 1;else echo 0;fi;clear >/dev/tty"); } } } } else if ( !isTerminalRunning() && terminalName() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return 0;} strcpy( lDialogString , terminalName() ) ; strcat( lDialogString , "'" ) ; if ( !gWarningDisplayed && !tinyfd_forceConsole) { gWarningDisplayed = 1 ; strcat( lDialogString , "echo \"" ) ; strcat( lDialogString, gTitle) ; strcat( lDialogString , "\";" ) ; strcat( lDialogString , "echo \"" ) ; strcat( lDialogString, tinyfd_needs) ; strcat( lDialogString , "\";echo;echo;" ) ; } if ( aTitle && strlen(aTitle) ) { strcat( lDialogString , "echo \"" ) ; strcat( lDialogString, aTitle) ; strcat( lDialogString , "\";echo;" ) ; } if ( aMessage && strlen(aMessage) ) { strcat( lDialogString , "echo \"" ) ; strcat( lDialogString, aMessage) ; strcat( lDialogString , "\"; " ) ; } if ( aDialogType && !strcmp("yesno",aDialogType) ) { strcat( lDialogString , "echo -n \"y/n: \"; " ) ; strcat( lDialogString , "stty sane -echo;" ) ; strcat( lDialogString , "answer=$( while ! head -c 1 | grep -i [ny];do true ;done);"); strcat( lDialogString , "if echo \"$answer\" | grep -iq \"^y\";then\n"); strcat( lDialogString , "\techo 1\nelse\n\techo 0\nfi" ) ; } else if ( aDialogType && !strcmp("okcancel",aDialogType) ) { strcat( lDialogString , "echo -n \"[O]kay/[C]ancel: \"; " ) ; strcat( lDialogString , "stty sane -echo;" ) ; strcat( lDialogString , "answer=$( while ! head -c 1 | grep -i [oc];do true ;done);"); strcat( lDialogString , "if echo \"$answer\" | grep -iq \"^o\";then\n"); strcat( lDialogString , "\techo 1\nelse\n\techo 0\nfi" ) ; } else if ( aDialogType && !strcmp("yesnocancel",aDialogType) ) { strcat( lDialogString , "echo -n \"[Y]es/[N]o/[C]ancel: \"; " ) ; strcat( lDialogString , "stty sane -echo;" ) ; strcat( lDialogString , "answer=$( while ! head -c 1 | grep -i [nyc];do true ;done);"); strcat( lDialogString , "if echo \"$answer\" | grep -iq \"^y\";then\n\techo 1\n"); strcat( lDialogString , "elif echo \"$answer\" | grep -iq \"^n\";then\n\techo 2\n" ) ; strcat( lDialogString , "else\n\techo 0\nfi" ) ; } else { strcat(lDialogString , "echo -n \"press enter to continue \"; "); strcat( lDialogString , "stty sane -echo;" ) ; strcat( lDialogString , "answer=$( while ! head -c 1;do true ;done);echo 1"); } strcat( lDialogString , " >/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt"); } else if ( !isTerminalRunning() && pythonDbusPresent() && !strcmp("ok" , aDialogType) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python-dbus");return 1;} strcpy( lDialogString , gPythonName ) ; strcat( lDialogString ," -c \"import dbus;bus=dbus.SessionBus();"); strcat( lDialogString ,"notif=bus.get_object('org.freedesktop.Notifications','/org/freedesktop/Notifications');" ) ; strcat( lDialogString ,"notify=dbus.Interface(notif,'org.freedesktop.Notifications');" ) ; strcat( lDialogString ,"notify.Notify('',0,'" ) ; if ( aIconType && strlen(aIconType) ) { strcat( lDialogString , aIconType ) ; } strcat(lDialogString, "','") ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, aTitle) ; } strcat(lDialogString, "','") ; if ( aMessage && strlen(aMessage) ) { lpDialogString = lDialogString + strlen(lDialogString); replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ; } strcat(lDialogString, "','','',5000)\"") ; } else if ( !isTerminalRunning() && (perlPresent() >= 2) && !strcmp("ok" , aDialogType) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"perl-dbus");return 1;} strcpy( lDialogString , "perl -e \"use Net::DBus;\ my \\$sessionBus = Net::DBus->session;\ my \\$notificationsService = \\$sessionBus->get_service('org.freedesktop.Notifications');\ my \\$notificationsObject = \\$notificationsService->get_object('/org/freedesktop/Notifications',\ 'org.freedesktop.Notifications');"); sprintf( lDialogString + strlen(lDialogString), "my \\$notificationId;\\$notificationId = \\$notificationsObject->Notify(shift, 0, '%s', '%s', '%s', [], {}, -1);\" ", aIconType?aIconType:"", aTitle?aTitle:"", aMessage?aMessage:"" ) ; } else if ( !isTerminalRunning() && notifysendPresent() && !strcmp("ok" , aDialogType) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"notifysend");return 1;} strcpy( lDialogString , "notify-send" ) ; if ( aIconType && strlen(aIconType) ) { strcat( lDialogString , " -i '" ) ; strcat( lDialogString , aIconType ) ; strcat( lDialogString , "'" ) ; } strcat( lDialogString , " \"" ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, aTitle) ; strcat( lDialogString , " | " ) ; } if ( aMessage && strlen(aMessage) ) { replaceSubStr( aMessage , "\n\t" , " | " , lBuff ) ; replaceSubStr( aMessage , "\n" , " | " , lBuff ) ; replaceSubStr( aMessage , "\t" , " " , lBuff ) ; strcat(lDialogString, lBuff) ; } strcat( lDialogString , "\"" ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return 0;} if ( !gWarningDisplayed && !tinyfd_forceConsole) { gWarningDisplayed = 1 ; printf("\n\n%s\n", gTitle); printf("%s\n\n", tinyfd_needs); } if ( aTitle && strlen(aTitle) ) { printf("\n%s\n", aTitle); } tcgetattr(0, &infoOri); tcgetattr(0, &info); info.c_lflag &= ~ICANON; info.c_cc[VMIN] = 1; info.c_cc[VTIME] = 0; tcsetattr(0, TCSANOW, &info); if ( aDialogType && !strcmp("yesno",aDialogType) ) { do { if ( aMessage && strlen(aMessage) ) { printf("\n%s\n",aMessage); } printf("y/n: "); fflush(stdout); lChar = tolower( getchar() ) ; printf("\n\n"); } while ( lChar != 'y' && lChar != 'n' ); lResult = lChar == 'y' ? 1 : 0 ; } else if ( aDialogType && !strcmp("okcancel",aDialogType) ) { do { if ( aMessage && strlen(aMessage) ) { printf("\n%s\n",aMessage); } printf("[O]kay/[C]ancel: "); fflush(stdout); lChar = tolower( getchar() ) ; printf("\n\n"); } while ( lChar != 'o' && lChar != 'c' ); lResult = lChar == 'o' ? 1 : 0 ; } else if ( aDialogType && !strcmp("yesnocancel",aDialogType) ) { do { if ( aMessage && strlen(aMessage) ) { printf("\n%s\n",aMessage); } printf("[Y]es/[N]o/[C]ancel: "); fflush(stdout); lChar = tolower( getchar() ) ; printf("\n\n"); } while ( lChar != 'y' && lChar != 'n' && lChar != 'c' ); lResult = (lChar == 'y') ? 1 : (lChar == 'n') ? 2 : 0 ; } else { if ( aMessage && strlen(aMessage) ) { printf("\n%s\n\n",aMessage); } printf("press enter to continue "); fflush(stdout); getchar() ; printf("\n\n"); lResult = 1 ; } tcsetattr(0, TCSANOW, &infoOri); free(lDialogString); return lResult ; } if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ; if ( ! ( lIn = popen( lDialogString , "r" ) ) ) { free(lDialogString); return 0 ; } while ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) {} pclose( lIn ) ; /* printf( "lBuff: %s len: %lu \n" , lBuff , strlen(lBuff) ) ; */ if ( lBuff[strlen( lBuff ) -1] == '\n' ) { lBuff[strlen( lBuff ) -1] = '\0' ; } /* printf( "lBuff1: %s len: %lu \n" , lBuff , strlen(lBuff) ) ; */ if (aDialogType && !strcmp("yesnocancel", aDialogType)) { if ( lBuff[0]=='1' ) { if ( !strcmp( lBuff+1 , "Yes" )) strcpy(lBuff,"1"); else if ( !strcmp( lBuff+1 , "No" )) strcpy(lBuff,"2"); } } /* printf( "lBuff2: %s len: %lu \n" , lBuff , strlen(lBuff) ) ; */ lResult = !strcmp( lBuff , "2" ) ? 2 : !strcmp( lBuff , "1" ) ? 1 : 0; /* printf( "lResult: %d\n" , lResult ) ; */ free(lDialogString); return lResult ; } /* return has only meaning for tinyfd_query */ int tinyfd_notifyPopup( char const * const aTitle , /* NULL or "" */ char const * const aMessage , /* NULL or "" may contain \n and \t */ char const * const aIconType ) /* "info" "warning" "error" */ { char lBuff[MAX_PATH_OR_CMD]; char * lDialogString = NULL ; char * lpDialogString ; FILE * lIn ; size_t lTitleLen ; size_t lMessageLen ; if ( getenv("SSH_TTY") ) { return tinyfd_messageBox(aTitle, aMessage, "ok", aIconType, 0); } lTitleLen = aTitle ? strlen(aTitle) : 0 ; lMessageLen = aMessage ? strlen(aMessage) : 0 ; if ( !aTitle || strcmp(aTitle,"tinyfd_query") ) { lDialogString = (char *) malloc( MAX_PATH_OR_CMD + lTitleLen + lMessageLen ); } if ( osascriptPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return 1;} strcpy( lDialogString , "osascript "); if ( ! osx9orBetter() ) strcat( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'"); strcat( lDialogString , " -e 'try' -e 'display notification \"") ; if ( aMessage && strlen(aMessage) ) { strcat(lDialogString, aMessage) ; } strcat(lDialogString, " \" ") ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "with title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } strcat( lDialogString, "' -e 'end try'") ; if ( ! osx9orBetter() ) strcat( lDialogString, " -e 'end tell'") ; } else if ( kdialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return 1;} strcpy( lDialogString , "kdialog" ) ; if ( aIconType && strlen(aIconType) ) { strcat( lDialogString , " --icon '" ) ; strcat( lDialogString , aIconType ) ; strcat( lDialogString , "'" ) ; } if ( aTitle && strlen(aTitle) ) { strcat( lDialogString , " --title \"" ) ; strcat( lDialogString , aTitle ) ; strcat( lDialogString , "\"" ) ; } strcat( lDialogString , " --passivepopup" ) ; strcat( lDialogString , " \"" ) ; if ( aMessage ) { strcat( lDialogString , aMessage ) ; } strcat( lDialogString , " \" 5" ) ; } else if ( (zenity3Present()>=5) || matedialogPresent() || shellementaryPresent() || qarmaPresent() ) { /* zenity 2.32 & 3.14 has the notification but with a bug: it doesnt return from it */ /* zenity 3.8 show the notification as an alert ok cancel box */ if ( zenity3Present()>=5 ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return 1;} strcpy( lDialogString , "zenity" ) ; } else if ( matedialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return 1;} strcpy( lDialogString , "matedialog" ) ; } else if ( shellementaryPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return 1;} strcpy( lDialogString , "shellementary" ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return 1;} strcpy( lDialogString , "qarma" ) ; } strcat( lDialogString , " --notification"); if ( aIconType && strlen( aIconType ) ) { strcat( lDialogString , " --window-icon '"); strcat( lDialogString , aIconType ) ; strcat( lDialogString , "'" ) ; } strcat( lDialogString , " --text \"" ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, aTitle) ; strcat(lDialogString, "\n") ; } if ( aMessage && strlen( aMessage ) ) { strcat( lDialogString , aMessage ) ; } strcat( lDialogString , " \"" ) ; } else if ( perlPresent() >= 2 ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"perl-dbus");return 1;} strcpy( lDialogString , "perl -e \"use Net::DBus;\ my \\$sessionBus = Net::DBus->session;\ my \\$notificationsService = \\$sessionBus->get_service('org.freedesktop.Notifications');\ my \\$notificationsObject = \\$notificationsService->get_object('/org/freedesktop/Notifications',\ 'org.freedesktop.Notifications');"); sprintf( lDialogString + strlen(lDialogString) , "my \\$notificationId;\\$notificationId = \\$notificationsObject->Notify(shift, 0, '%s', '%s', '%s', [], {}, -1);\" ", aIconType?aIconType:"", aTitle?aTitle:"", aMessage?aMessage:"" ) ; } else if ( pythonDbusPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python-dbus");return 1;} strcpy( lDialogString , gPythonName ) ; strcat( lDialogString ," -c \"import dbus;bus=dbus.SessionBus();"); strcat( lDialogString ,"notif=bus.get_object('org.freedesktop.Notifications','/org/freedesktop/Notifications');" ) ; strcat( lDialogString ,"notify=dbus.Interface(notif,'org.freedesktop.Notifications');" ) ; strcat( lDialogString ,"notify.Notify('',0,'" ) ; if ( aIconType && strlen(aIconType) ) { strcat( lDialogString , aIconType ) ; } strcat(lDialogString, "','") ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, aTitle) ; } strcat(lDialogString, "','") ; if ( aMessage && strlen(aMessage) ) { lpDialogString = lDialogString + strlen(lDialogString); replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ; } strcat(lDialogString, "','','',5000)\"") ; } else if ( notifysendPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"notifysend");return 1;} strcpy( lDialogString , "notify-send" ) ; if ( aIconType && strlen(aIconType) ) { strcat( lDialogString , " -i '" ) ; strcat( lDialogString , aIconType ) ; strcat( lDialogString , "'" ) ; } strcat( lDialogString , " \"" ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, aTitle) ; strcat( lDialogString , " | " ) ; } if ( aMessage && strlen(aMessage) ) { replaceSubStr( aMessage , "\n\t" , " | " , lBuff ) ; replaceSubStr( aMessage , "\n" , " | " , lBuff ) ; replaceSubStr( aMessage , "\t" , " " , lBuff ) ; strcat(lDialogString, lBuff) ; } strcat( lDialogString , "\"" ) ; } else { return tinyfd_messageBox(aTitle, aMessage, "ok", aIconType, 0); } if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ; if ( ! ( lIn = popen( lDialogString , "r" ) ) ) { free(lDialogString); return 0 ; } pclose( lIn ) ; free(lDialogString); return 1; } /* returns NULL on cancel */ char const * tinyfd_inputBox( char const * const aTitle , /* NULL or "" */ char const * const aMessage , /* NULL or "" may NOT contain \n nor \t */ char const * const aDefaultInput ) /* "" , if NULL it's a passwordBox */ { static char lBuff[MAX_PATH_OR_CMD]; char * lDialogString = NULL; char * lpDialogString; FILE * lIn ; int lResult ; int lWasGdialog = 0 ; int lWasGraphicDialog = 0 ; int lWasXterm = 0 ; int lWasBasicXterm = 0 ; struct termios oldt ; struct termios newt ; char * lEOF; size_t lTitleLen ; size_t lMessageLen ; lBuff[0]='\0'; lTitleLen = aTitle ? strlen(aTitle) : 0 ; lMessageLen = aMessage ? strlen(aMessage) : 0 ; if ( !aTitle || strcmp(aTitle,"tinyfd_query") ) { lDialogString = (char *) malloc( MAX_PATH_OR_CMD + lTitleLen + lMessageLen ); } if ( osascriptPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char const *)1;} strcpy( lDialogString , "osascript "); if ( ! osx9orBetter() ) strcat( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'"); strcat( lDialogString , " -e 'try' -e 'display dialog \"") ; if ( aMessage && strlen(aMessage) ) { strcat(lDialogString, aMessage) ; } strcat(lDialogString, "\" ") ; strcat(lDialogString, "default answer \"") ; if ( aDefaultInput && strlen(aDefaultInput) ) { strcat(lDialogString, aDefaultInput) ; } strcat(lDialogString, "\" ") ; if ( ! aDefaultInput ) { strcat(lDialogString, "hidden answer true ") ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "with title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } strcat(lDialogString, "with icon note' ") ; strcat(lDialogString, "-e '\"1\" & text returned of result' " ); strcat(lDialogString, "-e 'on error number -128' " ) ; strcat(lDialogString, "-e '0' " ); strcat(lDialogString, "-e 'end try'") ; if ( ! osx9orBetter() ) strcat(lDialogString, " -e 'end tell'") ; } else if ( kdialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char const *)1;} strcpy( lDialogString , "szAnswer=$(kdialog" ) ; if ( kdialogPresent() == 2 ) { strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } if ( ! aDefaultInput ) { strcat(lDialogString, " --password ") ; } else { strcat(lDialogString, " --inputbox ") ; } strcat(lDialogString, "\"") ; if ( aMessage && strlen(aMessage) ) { strcat(lDialogString, aMessage ) ; } strcat(lDialogString , "\" \"" ) ; if ( aDefaultInput && strlen(aDefaultInput) ) { strcat(lDialogString, aDefaultInput ) ; } strcat(lDialogString , "\"" ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, " --title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\"") ; } strcat( lDialogString , ");if [ $? = 0 ];then echo 1$szAnswer;else echo 0$szAnswer;fi"); } else if ( zenityPresent() || matedialogPresent() || shellementaryPresent() || qarmaPresent() ) { if ( zenityPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char const *)1;} strcpy( lDialogString , "szAnswer=$(zenity" ) ; if ( (zenity3Present() >= 4) && !getenv("SSH_TTY") ) { strcat( lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } } else if ( matedialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char const *)1;} strcpy( lDialogString , "szAnswer=$(matedialog" ) ; } else if ( shellementaryPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return (char const *)1;} strcpy( lDialogString , "szAnswer=$(shellementary" ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char const *)1;} strcpy( lDialogString , "szAnswer=$(qarma" ) ; if ( !getenv("SSH_TTY") ) { strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } } strcat( lDialogString ," --entry" ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, " --title=\"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\"") ; } if ( aMessage && strlen(aMessage) ) { strcat(lDialogString, " --text=\"") ; strcat(lDialogString, aMessage) ; strcat(lDialogString, "\"") ; } if ( aDefaultInput && strlen(aDefaultInput) ) { strcat(lDialogString, " --entry-text=\"") ; strcat(lDialogString, aDefaultInput) ; strcat(lDialogString, "\"") ; } else { strcat(lDialogString, " --hide-text") ; } if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null "); strcat( lDialogString , ");if [ $? = 0 ];then echo 1$szAnswer;else echo 0$szAnswer;fi"); } else if ( gxmessagePresent() || gmessagePresent() ) { if ( gxmessagePresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gxmessage");return (char const *)1;} strcpy( lDialogString , "szAnswer=$(gxmessage -buttons Ok:1,Cancel:0 -center \""); } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gmessage");return (char const *)1;} strcpy( lDialogString , "szAnswer=$(gmessage -buttons Ok:1,Cancel:0 -center \""); } if ( aMessage && strlen(aMessage) ) { strcat( lDialogString , aMessage ) ; } strcat(lDialogString, "\"" ) ; if ( aTitle && strlen(aTitle) ) { strcat( lDialogString , " -title \""); strcat( lDialogString , aTitle ) ; strcat(lDialogString, "\" " ) ; } strcat(lDialogString, " -entrytext \"" ) ; if ( aDefaultInput && strlen(aDefaultInput) ) { strcat( lDialogString , aDefaultInput ) ; } strcat(lDialogString, "\"" ) ; strcat( lDialogString , ");echo $?$szAnswer"); } else if ( !gdialogPresent() && !xdialogPresent() && tkinter2Present( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return (char const *)1;} strcpy( lDialogString , gPython2Name ) ; if ( ! isTerminalRunning( ) && isDarwin( ) ) { strcat( lDialogString , " -i" ) ; /* for osx without console */ } strcat( lDialogString , " -S -c \"import Tkinter,tkSimpleDialog;root=Tkinter.Tk();root.withdraw();"); if ( isDarwin( ) ) { strcat( lDialogString , "import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set \ frontmost of process \\\"Python\\\" to true' ''');"); } strcat( lDialogString ,"res=tkSimpleDialog.askstring(" ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "title='") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "',") ; } if ( aMessage && strlen(aMessage) ) { strcat(lDialogString, "prompt='") ; lpDialogString = lDialogString + strlen(lDialogString); replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ; strcat(lDialogString, "',") ; } if ( aDefaultInput ) { if ( strlen(aDefaultInput) ) { strcat(lDialogString, "initialvalue='") ; strcat(lDialogString, aDefaultInput) ; strcat(lDialogString, "',") ; } } else { strcat(lDialogString, "show='*'") ; } strcat(lDialogString, ");\nif res is None :\n\tprint 0"); strcat(lDialogString, "\nelse :\n\tprint '1'+res\n\"" ) ; } else if ( !gdialogPresent() && !xdialogPresent() && tkinter3Present( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return (char const *)1;} strcpy( lDialogString , gPython3Name ) ; strcat( lDialogString , " -S -c \"import tkinter; from tkinter import simpledialog;root=tkinter.Tk();root.withdraw();"); strcat( lDialogString ,"res=simpledialog.askstring(" ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "title='") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "',") ; } if ( aMessage && strlen(aMessage) ) { strcat(lDialogString, "prompt='") ; lpDialogString = lDialogString + strlen(lDialogString); replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ; strcat(lDialogString, "',") ; } if ( aDefaultInput ) { if ( strlen(aDefaultInput) ) { strcat(lDialogString, "initialvalue='") ; strcat(lDialogString, aDefaultInput) ; strcat(lDialogString, "',") ; } } else { strcat(lDialogString, "show='*'") ; } strcat(lDialogString, ");\nif res is None :\n\tprint(0)"); strcat(lDialogString, "\nelse :\n\tprint('1'+res)\n\"" ) ; } else if ( gdialogPresent() || xdialogPresent() || dialogName() || whiptailPresent() ) { if ( gdialogPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gdialog");return (char const *)1;} lWasGraphicDialog = 1 ; lWasGdialog = 1 ; strcpy( lDialogString , "(gdialog " ) ; } else if ( xdialogPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char const *)1;} lWasGraphicDialog = 1 ; strcpy( lDialogString , "(Xdialog " ) ; } else if ( dialogName( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} if ( isTerminalRunning( ) ) { strcpy( lDialogString , "(dialog " ) ; } else { lWasXterm = 1 ; strcpy( lDialogString , terminalName() ) ; strcat( lDialogString , "'(" ) ; strcat( lDialogString , dialogName() ) ; strcat( lDialogString , " " ) ; } } else if ( isTerminalRunning( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"whiptail");return (char const *)0;} strcpy( lDialogString , "(whiptail " ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"whiptail");return (char const *)0;} lWasXterm = 1 ; strcpy( lDialogString , terminalName() ) ; strcat( lDialogString , "'(whiptail " ) ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "--title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } if ( !xdialogPresent() && !gdialogPresent() ) { strcat(lDialogString, "--backtitle \"") ; strcat(lDialogString, "tab: move focus") ; if ( ! aDefaultInput && !lWasGdialog ) { strcat(lDialogString, " (sometimes nothing, no blink nor star, is shown in text field)") ; } strcat(lDialogString, "\" ") ; } if ( aDefaultInput || lWasGdialog ) { strcat( lDialogString , "--inputbox" ) ; } else { if ( !lWasGraphicDialog && dialogName() && isDialogVersionBetter09b() ) { strcat( lDialogString , "--insecure " ) ; } strcat( lDialogString , "--passwordbox" ) ; } strcat( lDialogString , " \"" ) ; if ( aMessage && strlen(aMessage) ) { strcat(lDialogString, aMessage) ; } strcat(lDialogString,"\" 10 60 ") ; if ( aDefaultInput && strlen(aDefaultInput) ) { strcat(lDialogString, "\"") ; strcat(lDialogString, aDefaultInput) ; strcat(lDialogString, "\" ") ; } if ( lWasGraphicDialog ) { strcat(lDialogString,") 2>/tmp/tinyfd.txt;\ if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;\ tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes") ; } else { strcat(lDialogString,">/dev/tty ) 2>/tmp/tinyfd.txt;\ if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;\ tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes") ; if ( lWasXterm ) { strcat(lDialogString," >/tmp/tinyfd0.txt';cat /tmp/tinyfd0.txt"); } else { strcat(lDialogString, "; clear >/dev/tty") ; } } } else if ( ! isTerminalRunning( ) && terminalName() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char const *)0;} lWasBasicXterm = 1 ; strcpy( lDialogString , terminalName() ) ; strcat( lDialogString , "'" ) ; if ( !gWarningDisplayed && !tinyfd_forceConsole) { gWarningDisplayed = 1 ; tinyfd_messageBox(gTitle,tinyfd_needs,"ok","warning",0); } if ( aTitle && strlen(aTitle) && !tinyfd_forceConsole) { strcat( lDialogString , "echo \"" ) ; strcat( lDialogString, aTitle) ; strcat( lDialogString , "\";echo;" ) ; } strcat( lDialogString , "echo \"" ) ; if ( aMessage && strlen(aMessage) ) { strcat( lDialogString, aMessage) ; } strcat( lDialogString , "\";read " ) ; if ( ! aDefaultInput ) { strcat( lDialogString , "-s " ) ; } strcat( lDialogString , "-p \"" ) ; strcat( lDialogString , "(esc+enter to cancel): \" ANSWER " ) ; strcat( lDialogString , ";echo 1$ANSWER >/tmp/tinyfd.txt';" ) ; strcat( lDialogString , "cat -v /tmp/tinyfd.txt"); } else if ( !gWarningDisplayed && ! isTerminalRunning( ) && ! terminalName() ) { gWarningDisplayed = 1 ; tinyfd_messageBox(gTitle,tinyfd_needs,"ok","warning",0); if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"no_solution");return (char const *)0;} return NULL; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char const *)0;} if ( !gWarningDisplayed && !tinyfd_forceConsole) { gWarningDisplayed = 1 ; tinyfd_messageBox(gTitle,tinyfd_needs,"ok","warning",0); } if ( aTitle && strlen(aTitle) ) { printf("\n%s\n", aTitle); } if ( aMessage && strlen(aMessage) ) { printf("\n%s\n",aMessage); } printf("(esc+enter to cancel): "); fflush(stdout); if ( ! aDefaultInput ) { tcgetattr(STDIN_FILENO, & oldt) ; newt = oldt ; newt.c_lflag &= ~ECHO ; tcsetattr(STDIN_FILENO, TCSANOW, & newt); } lEOF = fgets(lBuff, MAX_PATH_OR_CMD, stdin); /* printf("lbuff<%c><%d>\n",lBuff[0],lBuff[0]); */ if ( ! lEOF || (lBuff[0] == '\0') ) { free(lDialogString); return NULL; } if ( lBuff[0] == '\n' ) { lEOF = fgets(lBuff, MAX_PATH_OR_CMD, stdin); /* printf("lbuff<%c><%d>\n",lBuff[0],lBuff[0]); */ if ( ! lEOF || (lBuff[0] == '\0') ) { free(lDialogString); return NULL; } } if ( ! aDefaultInput ) { tcsetattr(STDIN_FILENO, TCSANOW, & oldt); printf("\n"); } printf("\n"); if ( strchr(lBuff,27) ) { free(lDialogString); return NULL ; } if ( lBuff[strlen( lBuff ) -1] == '\n' ) { lBuff[strlen( lBuff ) -1] = '\0' ; } free(lDialogString); return lBuff ; } if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ; lIn = popen( lDialogString , "r" ); if ( ! lIn ) { if ( fileExists("/tmp/tinyfd.txt") ) { wipefile("/tmp/tinyfd.txt"); remove("/tmp/tinyfd.txt"); } if ( fileExists("/tmp/tinyfd0.txt") ) { wipefile("/tmp/tinyfd0.txt"); remove("/tmp/tinyfd0.txt"); } free(lDialogString); return NULL ; } while ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) {} pclose( lIn ) ; if ( fileExists("/tmp/tinyfd.txt") ) { wipefile("/tmp/tinyfd.txt"); remove("/tmp/tinyfd.txt"); } if ( fileExists("/tmp/tinyfd0.txt") ) { wipefile("/tmp/tinyfd0.txt"); remove("/tmp/tinyfd0.txt"); } /* printf( "len Buff: %lu\n" , strlen(lBuff) ) ; */ /* printf( "lBuff0: %s\n" , lBuff ) ; */ if ( lBuff[strlen( lBuff ) -1] == '\n' ) { lBuff[strlen( lBuff ) -1] = '\0' ; } /* printf( "lBuff1: %s len: %lu \n" , lBuff , strlen(lBuff) ) ; */ if ( lWasBasicXterm ) { if ( strstr(lBuff,"^[") ) /* esc was pressed */ { free(lDialogString); return NULL ; } } lResult = strncmp( lBuff , "1" , 1) ? 0 : 1 ; /* printf( "lResult: %d \n" , lResult ) ; */ if ( ! lResult ) { free(lDialogString); return NULL ; } /* printf( "lBuff+1: %s\n" , lBuff+1 ) ; */ free(lDialogString); return lBuff+1 ; } char const * tinyfd_saveFileDialog( char const * const aTitle , /* NULL or "" */ char const * const aDefaultPathAndFile , /* NULL or "" */ int const aNumOfFilterPatterns , /* 0 */ char const * const * const aFilterPatterns , /* NULL or {"*.jpg","*.png"} */ char const * const aSingleFilterDescription ) /* NULL or "image files" */ { static char lBuff [MAX_PATH_OR_CMD] ; char lDialogString [MAX_PATH_OR_CMD] ; char lString [MAX_PATH_OR_CMD] ; int i ; int lWasGraphicDialog = 0 ; int lWasXterm = 0 ; char const * p ; FILE * lIn ; lBuff[0]='\0'; if ( osascriptPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char const *)1;} strcpy( lDialogString , "osascript "); if ( ! osx9orBetter() ) strcat( lDialogString , " -e 'tell application \"Finder\"' -e 'Activate'"); strcat( lDialogString , " -e 'try' -e 'POSIX path of ( choose file name " ); if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "with prompt \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } getPathWithoutFinalSlash( lString , aDefaultPathAndFile ) ; if ( strlen(lString) ) { strcat(lDialogString, "default location \"") ; strcat(lDialogString, lString ) ; strcat(lDialogString , "\" " ) ; } getLastName( lString , aDefaultPathAndFile ) ; if ( strlen(lString) ) { strcat(lDialogString, "default name \"") ; strcat(lDialogString, lString ) ; strcat(lDialogString , "\" " ) ; } strcat( lDialogString , ")' " ) ; strcat(lDialogString, "-e 'on error number -128' " ) ; strcat(lDialogString, "-e 'end try'") ; if ( ! osx9orBetter() ) strcat( lDialogString, " -e 'end tell'") ; } else if ( kdialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char const *)1;} strcpy( lDialogString , "kdialog" ) ; if ( kdialogPresent() == 2 ) { strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } strcat( lDialogString , " --getsavefilename " ) ; if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) { if ( aDefaultPathAndFile[0] != '/' ) { strcat(lDialogString, "$PWD/") ; } strcat(lDialogString, "\"") ; strcat(lDialogString, aDefaultPathAndFile ) ; strcat(lDialogString , "\"" ) ; } else { strcat(lDialogString, "$PWD/") ; } if ( aNumOfFilterPatterns > 0 ) { strcat(lDialogString , " \"" ) ; for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) { strcat( lDialogString , aFilterPatterns [i] ) ; strcat( lDialogString , " " ) ; } if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) { strcat( lDialogString , " | " ) ; strcat( lDialogString , aSingleFilterDescription ) ; } strcat( lDialogString , "\"" ) ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, " --title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\"") ; } } else if ( zenityPresent() || matedialogPresent() || shellementaryPresent() || qarmaPresent() ) { if ( zenityPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char const *)1;} strcpy( lDialogString , "zenity" ) ; if ( (zenity3Present() >= 4) && !getenv("SSH_TTY") ) { strcat( lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } } else if ( matedialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char const *)1;} strcpy( lDialogString , "matedialog" ) ; } else if ( shellementaryPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return (char const *)1;} strcpy( lDialogString , "shellementary" ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char const *)1;} strcpy( lDialogString , "qarma" ) ; if ( !getenv("SSH_TTY") ) { strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } } strcat(lDialogString, " --file-selection --save --confirm-overwrite" ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, " --title=\"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\"") ; } if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) { strcat(lDialogString, " --filename=\"") ; strcat(lDialogString, aDefaultPathAndFile) ; strcat(lDialogString, "\"") ; } if ( aNumOfFilterPatterns > 0 ) { strcat( lDialogString , " --file-filter='" ) ; if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) { strcat( lDialogString , aSingleFilterDescription ) ; strcat( lDialogString , " | " ) ; } for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) { strcat( lDialogString , aFilterPatterns [i] ) ; strcat( lDialogString , " " ) ; } strcat( lDialogString , "' --file-filter='All files | *'" ) ; } if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null "); } else if ( !xdialogPresent() && tkinter2Present( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return (char const *)1;} strcpy( lDialogString , gPython2Name ) ; if ( ! isTerminalRunning( ) && isDarwin( )) { strcat( lDialogString , " -i" ) ; /* for osx without console */ } strcat( lDialogString , " -S -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();"); if ( isDarwin( ) ) { strcat( lDialogString , "import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set\ frontmost of process \\\"Python\\\" to true' ''');"); } strcat( lDialogString , "print tkFileDialog.asksaveasfilename("); if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "title='") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "',") ; } if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) { getPathWithoutFinalSlash( lString , aDefaultPathAndFile ) ; if ( strlen(lString) ) { strcat(lDialogString, "initialdir='") ; strcat(lDialogString, lString ) ; strcat(lDialogString , "'," ) ; } getLastName( lString , aDefaultPathAndFile ) ; if ( strlen(lString) ) { strcat(lDialogString, "initialfile='") ; strcat(lDialogString, lString ) ; strcat(lDialogString , "'," ) ; } } if ( ( aNumOfFilterPatterns > 1 ) || ( (aNumOfFilterPatterns == 1) /* test because poor osx behaviour */ && ( aFilterPatterns[0][strlen(aFilterPatterns[0])-1] != '*' ) ) ) { strcat(lDialogString , "filetypes=(" ) ; strcat( lDialogString , "('" ) ; if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) { strcat( lDialogString , aSingleFilterDescription ) ; } strcat( lDialogString , "',(" ) ; for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) { strcat( lDialogString , "'" ) ; strcat( lDialogString , aFilterPatterns [i] ) ; strcat( lDialogString , "'," ) ; } strcat( lDialogString , "))," ) ; strcat( lDialogString , "('All files','*'))" ) ; } strcat( lDialogString , ")\"" ) ; } else if ( !xdialogPresent() && tkinter3Present( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return (char const *)1;} strcpy( lDialogString , gPython3Name ) ; strcat( lDialogString , " -S -c \"import tkinter;from tkinter import filedialog;root=tkinter.Tk();root.withdraw();"); strcat( lDialogString , "print( filedialog.asksaveasfilename("); if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "title='") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "',") ; } if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) { getPathWithoutFinalSlash( lString , aDefaultPathAndFile ) ; if ( strlen(lString) ) { strcat(lDialogString, "initialdir='") ; strcat(lDialogString, lString ) ; strcat(lDialogString , "'," ) ; } getLastName( lString , aDefaultPathAndFile ) ; if ( strlen(lString) ) { strcat(lDialogString, "initialfile='") ; strcat(lDialogString, lString ) ; strcat(lDialogString , "'," ) ; } } if ( ( aNumOfFilterPatterns > 1 ) || ( (aNumOfFilterPatterns == 1) /* test because poor osx behaviour */ && ( aFilterPatterns[0][strlen(aFilterPatterns[0])-1] != '*' ) ) ) { strcat(lDialogString , "filetypes=(" ) ; strcat( lDialogString , "('" ) ; if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) { strcat( lDialogString , aSingleFilterDescription ) ; } strcat( lDialogString , "',(" ) ; for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) { strcat( lDialogString , "'" ) ; strcat( lDialogString , aFilterPatterns [i] ) ; strcat( lDialogString , "'," ) ; } strcat( lDialogString , "))," ) ; strcat( lDialogString , "('All files','*'))" ) ; } strcat( lDialogString , "))\"" ) ; } else if ( xdialogPresent() || dialogName() ) { if ( xdialogPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char const *)1;} lWasGraphicDialog = 1 ; strcpy( lDialogString , "(Xdialog " ) ; } else if ( isTerminalRunning( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} strcpy( lDialogString , "(dialog " ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} lWasXterm = 1 ; strcpy( lDialogString , terminalName() ) ; strcat( lDialogString , "'(" ) ; strcat( lDialogString , dialogName() ) ; strcat( lDialogString , " " ) ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "--title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } if ( !xdialogPresent() && !gdialogPresent() ) { strcat(lDialogString, "--backtitle \"") ; strcat(lDialogString, "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ; strcat(lDialogString, "\" ") ; } strcat( lDialogString , "--fselect \"" ) ; if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) { if ( ! strchr(aDefaultPathAndFile, '/') ) { strcat(lDialogString, "./") ; } strcat(lDialogString, aDefaultPathAndFile) ; } else if ( ! isTerminalRunning( ) && !lWasGraphicDialog ) { strcat(lDialogString, getenv("HOME")) ; strcat(lDialogString, "/") ; } else { strcat(lDialogString, "./") ; } if ( lWasGraphicDialog ) { strcat(lDialogString, "\" 0 60 ) 2>&1 ") ; } else { strcat(lDialogString, "\" 0 60 >/dev/tty) ") ; if ( lWasXterm ) { strcat( lDialogString , "2>/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt"); } else { strcat(lDialogString, "2>&1 ; clear >/dev/tty") ; } } } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){return tinyfd_inputBox(aTitle,NULL,NULL);} p = tinyfd_inputBox( aTitle , "Save file" , "" ) ; getPathWithoutFinalSlash( lString , p ) ; if ( strlen( lString ) && ! dirExists( lString ) ) { return NULL ; } getLastName(lString,p); if ( ! strlen(lString) ) { return NULL; } return p ; } if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ; if ( ! ( lIn = popen( lDialogString , "r" ) ) ) { return NULL ; } while ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) {} pclose( lIn ) ; if ( lBuff[strlen( lBuff ) -1] == '\n' ) { lBuff[strlen( lBuff ) -1] = '\0' ; } /* printf( "lBuff: %s\n" , lBuff ) ; */ if ( ! strlen(lBuff) ) { return NULL; } getPathWithoutFinalSlash( lString , lBuff ) ; if ( strlen( lString ) && ! dirExists( lString ) ) { return NULL ; } getLastName(lString,lBuff); if ( ! filenameValid(lString) ) { return NULL; } return lBuff ; } /* in case of multiple files, the separator is | */ char const * tinyfd_openFileDialog( char const * const aTitle , /* NULL or "" */ char const * const aDefaultPathAndFile , /* NULL or "" */ int const aNumOfFilterPatterns , /* 0 */ char const * const * const aFilterPatterns , /* NULL or {"*.jpg","*.png"} */ char const * const aSingleFilterDescription , /* NULL or "image files" */ int const aAllowMultipleSelects ) /* 0 or 1 */ { static char lBuff [MAX_MULTIPLE_FILES*MAX_PATH_OR_CMD] ; char lDialogString [MAX_PATH_OR_CMD] ; char lString [MAX_PATH_OR_CMD] ; int i ; FILE * lIn ; char * p ; char const * p2 ; int lWasKdialog = 0 ; int lWasGraphicDialog = 0 ; int lWasXterm = 0 ; lBuff[0]='\0'; if ( osascriptPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char const *)1;} strcpy( lDialogString , "osascript "); if ( ! osx9orBetter() ) strcat( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'"); strcat( lDialogString , " -e 'try' -e '" ); if ( ! aAllowMultipleSelects ) { strcat( lDialogString , "POSIX path of ( " ); } else { strcat( lDialogString , "set mylist to " ); } strcat( lDialogString , "choose file " ); if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "with prompt \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } getPathWithoutFinalSlash( lString , aDefaultPathAndFile ) ; if ( strlen(lString) ) { strcat(lDialogString, "default location \"") ; strcat(lDialogString, lString ) ; strcat(lDialogString , "\" " ) ; } if ( aNumOfFilterPatterns > 0 ) { strcat(lDialogString , "of type {\"" ); strcat( lDialogString , aFilterPatterns [0] + 2 ) ; strcat( lDialogString , "\"" ) ; for ( i = 1 ; i < aNumOfFilterPatterns ; i ++ ) { strcat( lDialogString , ",\"" ) ; strcat( lDialogString , aFilterPatterns [i] + 2) ; strcat( lDialogString , "\"" ) ; } strcat( lDialogString , "} " ) ; } if ( aAllowMultipleSelects ) { strcat( lDialogString , "multiple selections allowed true ' " ) ; strcat( lDialogString , "-e 'set mystring to POSIX path of item 1 of mylist' " ); strcat( lDialogString , "-e 'repeat with i from 2 to the count of mylist' " ); strcat( lDialogString , "-e 'set mystring to mystring & \"|\"' " ); strcat( lDialogString , "-e 'set mystring to mystring & POSIX path of item i of mylist' " ); strcat( lDialogString , "-e 'end repeat' " ); strcat( lDialogString , "-e 'mystring' " ); } else { strcat( lDialogString , ")' " ) ; } strcat(lDialogString, "-e 'on error number -128' " ) ; strcat(lDialogString, "-e 'end try'") ; if ( ! osx9orBetter() ) strcat( lDialogString, " -e 'end tell'") ; } else if ( kdialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char const *)1;} lWasKdialog = 1 ; strcpy( lDialogString , "kdialog" ) ; if ( kdialogPresent() == 2 ) { strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } strcat( lDialogString , " --getopenfilename " ) ; if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) { if ( aDefaultPathAndFile[0] != '/' ) { strcat(lDialogString, "$PWD/") ; } strcat(lDialogString, "\"") ; strcat(lDialogString, aDefaultPathAndFile ) ; strcat(lDialogString , "\"" ) ; } else { strcat(lDialogString, "$PWD/") ; } if ( aNumOfFilterPatterns > 0 ) { strcat(lDialogString , " \"" ) ; for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) { strcat( lDialogString , aFilterPatterns [i] ) ; strcat( lDialogString , " " ) ; } if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) { strcat( lDialogString , " | " ) ; strcat( lDialogString , aSingleFilterDescription ) ; } strcat( lDialogString , "\"" ) ; } if ( aAllowMultipleSelects ) { strcat( lDialogString , " --multiple --separate-output" ) ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, " --title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\"") ; } } else if ( zenityPresent() || matedialogPresent() || shellementaryPresent() || qarmaPresent() ) { if ( zenityPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char const *)1;} strcpy( lDialogString , "zenity" ) ; if ( (zenity3Present() >= 4) && !getenv("SSH_TTY") ) { strcat( lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } } else if ( matedialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char const *)1;} strcpy( lDialogString , "matedialog" ) ; } else if ( shellementaryPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return (char const *)1;} strcpy( lDialogString , "shellementary" ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char const *)1;} strcpy( lDialogString , "qarma" ) ; if ( !getenv("SSH_TTY") ) { strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } } strcat( lDialogString , " --file-selection" ) ; if ( aAllowMultipleSelects ) { strcat( lDialogString , " --multiple" ) ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, " --title=\"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\"") ; } if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) { strcat(lDialogString, " --filename=\"") ; strcat(lDialogString, aDefaultPathAndFile) ; strcat(lDialogString, "\"") ; } if ( aNumOfFilterPatterns > 0 ) { strcat( lDialogString , " --file-filter='" ) ; if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) { strcat( lDialogString , aSingleFilterDescription ) ; strcat( lDialogString , " | " ) ; } for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) { strcat( lDialogString , aFilterPatterns [i] ) ; strcat( lDialogString , " " ) ; } strcat( lDialogString , "' --file-filter='All files | *'" ) ; } if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null "); } else if ( tkinter2Present( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return (char const *)1;} strcpy( lDialogString , gPython2Name ) ; if ( ! isTerminalRunning( ) && isDarwin( ) ) { strcat( lDialogString , " -i" ) ; /* for osx without console */ } strcat( lDialogString , " -S -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();"); if ( isDarwin( ) ) { strcat( lDialogString , "import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set \ frontmost of process \\\"Python\\\" to true' ''');"); } strcat( lDialogString , "lFiles=tkFileDialog.askopenfilename("); if ( aAllowMultipleSelects ) { strcat( lDialogString , "multiple=1," ) ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "title='") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "',") ; } if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) { getPathWithoutFinalSlash( lString , aDefaultPathAndFile ) ; if ( strlen(lString) ) { strcat(lDialogString, "initialdir='") ; strcat(lDialogString, lString ) ; strcat(lDialogString , "'," ) ; } getLastName( lString , aDefaultPathAndFile ) ; if ( strlen(lString) ) { strcat(lDialogString, "initialfile='") ; strcat(lDialogString, lString ) ; strcat(lDialogString , "'," ) ; } } if ( ( aNumOfFilterPatterns > 1 ) || ( ( aNumOfFilterPatterns == 1 ) /*test because poor osx behaviour*/ && ( aFilterPatterns[0][strlen(aFilterPatterns[0])-1] != '*' ) ) ) { strcat(lDialogString , "filetypes=(" ) ; strcat( lDialogString , "('" ) ; if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) { strcat( lDialogString , aSingleFilterDescription ) ; } strcat( lDialogString , "',(" ) ; for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) { strcat( lDialogString , "'" ) ; strcat( lDialogString , aFilterPatterns [i] ) ; strcat( lDialogString , "'," ) ; } strcat( lDialogString , "))," ) ; strcat( lDialogString , "('All files','*'))" ) ; } strcat( lDialogString , ");\ \nif not isinstance(lFiles, tuple):\n\tprint lFiles\nelse:\ \n\tlFilesString=''\n\tfor lFile in lFiles:\n\t\tlFilesString+=str(lFile)+'|'\ \n\tprint lFilesString[:-1]\n\"" ) ; } else if ( tkinter3Present( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return (char const *)1;} strcpy( lDialogString , gPython3Name ) ; strcat( lDialogString , " -S -c \"import tkinter;from tkinter import filedialog;root=tkinter.Tk();root.withdraw();"); strcat( lDialogString , "lFiles=filedialog.askopenfilename("); if ( aAllowMultipleSelects ) { strcat( lDialogString , "multiple=1," ) ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "title='") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "',") ; } if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) { getPathWithoutFinalSlash( lString , aDefaultPathAndFile ) ; if ( strlen(lString) ) { strcat(lDialogString, "initialdir='") ; strcat(lDialogString, lString ) ; strcat(lDialogString , "'," ) ; } getLastName( lString , aDefaultPathAndFile ) ; if ( strlen(lString) ) { strcat(lDialogString, "initialfile='") ; strcat(lDialogString, lString ) ; strcat(lDialogString , "'," ) ; } } if ( ( aNumOfFilterPatterns > 1 ) || ( ( aNumOfFilterPatterns == 1 ) /*test because poor osx behaviour*/ && ( aFilterPatterns[0][strlen(aFilterPatterns[0])-1] != '*' ) ) ) { strcat(lDialogString , "filetypes=(" ) ; strcat( lDialogString , "('" ) ; if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) { strcat( lDialogString , aSingleFilterDescription ) ; } strcat( lDialogString , "',(" ) ; for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) { strcat( lDialogString , "'" ) ; strcat( lDialogString , aFilterPatterns [i] ) ; strcat( lDialogString , "'," ) ; } strcat( lDialogString , "))," ) ; strcat( lDialogString , "('All files','*'))" ) ; } strcat( lDialogString , ");\ \nif not isinstance(lFiles, tuple):\n\tprint(lFiles)\nelse:\ \n\tlFilesString=''\n\tfor lFile in lFiles:\n\t\tlFilesString+=str(lFile)+'|'\ \n\tprint(lFilesString[:-1])\n\"" ) ; } else if ( xdialogPresent() || dialogName() ) { if ( xdialogPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char const *)1;} lWasGraphicDialog = 1 ; strcpy( lDialogString , "(Xdialog " ) ; } else if ( isTerminalRunning( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} strcpy( lDialogString , "(dialog " ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} lWasXterm = 1 ; strcpy( lDialogString , terminalName() ) ; strcat( lDialogString , "'(" ) ; strcat( lDialogString , dialogName() ) ; strcat( lDialogString , " " ) ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "--title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } if ( !xdialogPresent() && !gdialogPresent() ) { strcat(lDialogString, "--backtitle \"") ; strcat(lDialogString, "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ; strcat(lDialogString, "\" ") ; } strcat( lDialogString , "--fselect \"" ) ; if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) { if ( ! strchr(aDefaultPathAndFile, '/') ) { strcat(lDialogString, "./") ; } strcat(lDialogString, aDefaultPathAndFile) ; } else if ( ! isTerminalRunning( ) && !lWasGraphicDialog ) { strcat(lDialogString, getenv("HOME")) ; strcat(lDialogString, "/"); } else { strcat(lDialogString, "./") ; } if ( lWasGraphicDialog ) { strcat(lDialogString, "\" 0 60 ) 2>&1 ") ; } else { strcat(lDialogString, "\" 0 60 >/dev/tty) ") ; if ( lWasXterm ) { strcat( lDialogString , "2>/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt"); } else { strcat(lDialogString, "2>&1 ; clear >/dev/tty") ; } } } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){return tinyfd_inputBox(aTitle,NULL,NULL);} p2 = tinyfd_inputBox(aTitle, "Open file",""); if ( ! fileExists(p2) ) { return NULL ; } return p2 ; } if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ; if ( ! ( lIn = popen( lDialogString , "r" ) ) ) { return NULL ; } lBuff[0]='\0'; p=lBuff; while ( fgets( p , sizeof( lBuff ) , lIn ) != NULL ) { p += strlen( p ); } pclose( lIn ) ; if ( lBuff[strlen( lBuff ) -1] == '\n' ) { lBuff[strlen( lBuff ) -1] = '\0' ; } /* printf( "lBuff: %s\n" , lBuff ) ; */ if ( lWasKdialog && aAllowMultipleSelects ) { p = lBuff ; while ( ( p = strchr( p , '\n' ) ) ) * p = '|' ; } /* printf( "lBuff2: %s\n" , lBuff ) ; */ if ( ! strlen( lBuff ) ) { return NULL; } if ( aAllowMultipleSelects && strchr(lBuff, '|') ) { p2 = ensureFilesExist( lBuff , lBuff ) ; } else if ( fileExists(lBuff) ) { p2 = lBuff ; } else { return NULL ; } /* printf( "lBuff3: %s\n" , p2 ) ; */ return p2 ; } char const * tinyfd_selectFolderDialog( char const * const aTitle , /* "" */ char const * const aDefaultPath ) /* "" */ { static char lBuff [MAX_PATH_OR_CMD] ; char lDialogString [MAX_PATH_OR_CMD] ; FILE * lIn ; char const * p ; int lWasGraphicDialog = 0 ; int lWasXterm = 0 ; lBuff[0]='\0'; if ( osascriptPresent( )) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char const *)1;} strcpy( lDialogString , "osascript "); if ( ! osx9orBetter() ) strcat( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'"); strcat( lDialogString , " -e 'try' -e 'POSIX path of ( choose folder "); if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "with prompt \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } if ( aDefaultPath && strlen(aDefaultPath) ) { strcat(lDialogString, "default location \"") ; strcat(lDialogString, aDefaultPath ) ; strcat(lDialogString , "\" " ) ; } strcat( lDialogString , ")' " ) ; strcat(lDialogString, "-e 'on error number -128' " ) ; strcat(lDialogString, "-e 'end try'") ; if ( ! osx9orBetter() ) strcat( lDialogString, " -e 'end tell'") ; } else if ( kdialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char const *)1;} strcpy( lDialogString , "kdialog" ) ; if ( kdialogPresent() == 2 ) { strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } strcat( lDialogString , " --getexistingdirectory " ) ; if ( aDefaultPath && strlen(aDefaultPath) ) { if ( aDefaultPath[0] != '/' ) { strcat(lDialogString, "$PWD/") ; } strcat(lDialogString, "\"") ; strcat(lDialogString, aDefaultPath ) ; strcat(lDialogString , "\"" ) ; } else { strcat(lDialogString, "$PWD/") ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, " --title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\"") ; } } else if ( zenityPresent() || matedialogPresent() || shellementaryPresent() || qarmaPresent() ) { if ( zenityPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char const *)1;} strcpy( lDialogString , "zenity" ) ; if ( (zenity3Present() >= 4) && !getenv("SSH_TTY") ) { strcat( lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } } else if ( matedialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char const *)1;} strcpy( lDialogString , "matedialog" ) ; } else if ( shellementaryPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return (char const *)1;} strcpy( lDialogString , "shellementary" ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char const *)1;} strcpy( lDialogString , "qarma" ) ; if ( !getenv("SSH_TTY") ) { strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } } strcat( lDialogString , " --file-selection --directory" ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, " --title=\"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\"") ; } if ( aDefaultPath && strlen(aDefaultPath) ) { strcat(lDialogString, " --filename=\"") ; strcat(lDialogString, aDefaultPath) ; strcat(lDialogString, "\"") ; } if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null "); } else if ( !xdialogPresent() && tkinter2Present( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return (char const *)1;} strcpy( lDialogString , gPython2Name ) ; if ( ! isTerminalRunning( ) && isDarwin( ) ) { strcat( lDialogString , " -i" ) ; /* for osx without console */ } strcat( lDialogString , " -S -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();"); if ( isDarwin( ) ) { strcat( lDialogString , "import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set \ frontmost of process \\\"Python\\\" to true' ''');"); } strcat( lDialogString , "print tkFileDialog.askdirectory("); if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "title='") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "',") ; } if ( aDefaultPath && strlen(aDefaultPath) ) { strcat(lDialogString, "initialdir='") ; strcat(lDialogString, aDefaultPath ) ; strcat(lDialogString , "'" ) ; } strcat( lDialogString , ")\"" ) ; } else if ( !xdialogPresent() && tkinter3Present( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return (char const *)1;} strcpy( lDialogString , gPython3Name ) ; strcat( lDialogString , " -S -c \"import tkinter;from tkinter import filedialog;root=tkinter.Tk();root.withdraw();"); strcat( lDialogString , "print( filedialog.askdirectory("); if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "title='") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "',") ; } if ( aDefaultPath && strlen(aDefaultPath) ) { strcat(lDialogString, "initialdir='") ; strcat(lDialogString, aDefaultPath ) ; strcat(lDialogString , "'" ) ; } strcat( lDialogString , ") )\"" ) ; } else if ( xdialogPresent() || dialogName() ) { if ( xdialogPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char const *)1;} lWasGraphicDialog = 1 ; strcpy( lDialogString , "(Xdialog " ) ; } else if ( isTerminalRunning( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} strcpy( lDialogString , "(dialog " ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char const *)0;} lWasXterm = 1 ; strcpy( lDialogString , terminalName() ) ; strcat( lDialogString , "'(" ) ; strcat( lDialogString , dialogName() ) ; strcat( lDialogString , " " ) ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "--title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } if ( !xdialogPresent() && !gdialogPresent() ) { strcat(lDialogString, "--backtitle \"") ; strcat(lDialogString, "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ; strcat(lDialogString, "\" ") ; } strcat( lDialogString , "--dselect \"" ) ; if ( aDefaultPath && strlen(aDefaultPath) ) { strcat(lDialogString, aDefaultPath) ; ensureFinalSlash(lDialogString); } else if ( ! isTerminalRunning( ) && !lWasGraphicDialog ) { strcat(lDialogString, getenv("HOME")) ; strcat(lDialogString, "/"); } else { strcat(lDialogString, "./") ; } if ( lWasGraphicDialog ) { strcat(lDialogString, "\" 0 60 ) 2>&1 ") ; } else { strcat(lDialogString, "\" 0 60 >/dev/tty) ") ; if ( lWasXterm ) { strcat( lDialogString , "2>/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt"); } else { strcat(lDialogString, "2>&1 ; clear >/dev/tty") ; } } } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){return tinyfd_inputBox(aTitle,NULL,NULL);} p = tinyfd_inputBox(aTitle, "Select folder",""); if ( !p || ! strlen( p ) || ! dirExists( p ) ) { return NULL ; } return p ; } if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ; if ( ! ( lIn = popen( lDialogString , "r" ) ) ) { return NULL ; } while ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) {} pclose( lIn ) ; if ( lBuff[strlen( lBuff ) -1] == '\n' ) { lBuff[strlen( lBuff ) -1] = '\0' ; } /* printf( "lBuff: %s\n" , lBuff ) ; */ if ( ! strlen( lBuff ) || ! dirExists( lBuff ) ) { return NULL ; } return lBuff ; } /* returns the hexcolor as a string "#FF0000" */ /* aoResultRGB also contains the result */ /* aDefaultRGB is used only if aDefaultHexRGB is NULL */ /* aDefaultRGB and aoResultRGB can be the same array */ char const * tinyfd_colorChooser( char const * const aTitle , /* NULL or "" */ char const * const aDefaultHexRGB , /* NULL or "#FF0000"*/ unsigned char const aDefaultRGB[3] , /* { 0 , 255 , 255 } */ unsigned char aoResultRGB[3] ) /* { 0 , 0 , 0 } */ { static char lBuff [128] ; char lTmp [128] ; #if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L char * lTmp2 ; #endif char lDialogString [MAX_PATH_OR_CMD] ; char lDefaultHexRGB[8]; char * lpDefaultHexRGB; unsigned char lDefaultRGB[3]; char const * p; FILE * lIn ; int i ; int lWasZenity3 = 0 ; int lWasOsascript = 0 ; int lWasXdialog = 0 ; lBuff[0]='\0'; if ( aDefaultHexRGB ) { Hex2RGB( aDefaultHexRGB , lDefaultRGB ) ; lpDefaultHexRGB = (char *) aDefaultHexRGB ; } else { lDefaultRGB[0]=aDefaultRGB[0]; lDefaultRGB[1]=aDefaultRGB[1]; lDefaultRGB[2]=aDefaultRGB[2]; RGB2Hex( aDefaultRGB , lDefaultHexRGB ) ; lpDefaultHexRGB = (char *) lDefaultHexRGB ; } if ( osascriptPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char const *)1;} lWasOsascript = 1 ; strcpy( lDialogString , "osascript"); if ( ! osx9orBetter() ) { strcat( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'"); strcat( lDialogString , " -e 'try' -e 'set mycolor to choose color default color {"); } else { strcat( lDialogString , " -e 'try' -e 'tell app (path to frontmost application as Unicode text) \ to set mycolor to choose color default color {"); } sprintf(lTmp, "%d", 256 * lDefaultRGB[0] ) ; strcat(lDialogString, lTmp ) ; strcat(lDialogString, "," ) ; sprintf(lTmp, "%d", 256 * lDefaultRGB[1] ) ; strcat(lDialogString, lTmp ) ; strcat(lDialogString, "," ) ; sprintf(lTmp, "%d", 256 * lDefaultRGB[2] ) ; strcat(lDialogString, lTmp ) ; strcat(lDialogString, "}' " ) ; strcat( lDialogString , "-e 'set mystring to ((item 1 of mycolor) div 256 as integer) as string' " ); strcat( lDialogString , "-e 'repeat with i from 2 to the count of mycolor' " ); strcat( lDialogString , "-e 'set mystring to mystring & \" \" & ((item i of mycolor) div 256 as integer) as string' " ); strcat( lDialogString , "-e 'end repeat' " ); strcat( lDialogString , "-e 'mystring' "); strcat(lDialogString, "-e 'on error number -128' " ) ; strcat(lDialogString, "-e 'end try'") ; if ( ! osx9orBetter() ) strcat( lDialogString, " -e 'end tell'") ; } else if ( kdialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char const *)1;} strcpy( lDialogString , "kdialog" ) ; if ( kdialogPresent() == 2 ) { strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } sprintf( lDialogString + strlen(lDialogString) , " --getcolor --default '%s'" , lpDefaultHexRGB ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, " --title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\"") ; } } else if ( zenity3Present() || matedialogPresent() || shellementaryPresent() || qarmaPresent() ) { lWasZenity3 = 1 ; if ( zenity3Present() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity3");return (char const *)1;} strcpy( lDialogString , "zenity" ); if ( (zenity3Present() >= 4) && !getenv("SSH_TTY") ) { strcat( lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } } else if ( matedialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char const *)1;} strcpy( lDialogString , "matedialog" ) ; } else if ( shellementaryPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return (char const *)1;} strcpy( lDialogString , "shellementary" ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char const *)1;} strcpy( lDialogString , "qarma" ) ; if ( !getenv("SSH_TTY") ) { strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } } strcat( lDialogString , " --color-selection --show-palette" ) ; sprintf( lDialogString + strlen(lDialogString), " --color=%s" , lpDefaultHexRGB ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, " --title=\"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\"") ; } if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null "); } else if ( xdialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char const *)1;} lWasXdialog = 1 ; strcpy( lDialogString , "Xdialog --colorsel \"" ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, aTitle) ; } strcat(lDialogString, "\" 0 60 ") ; #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L sprintf(lTmp,"%hhu %hhu %hhu",lDefaultRGB[0],lDefaultRGB[1],lDefaultRGB[2]); #else sprintf(lTmp,"%hu %hu %hu",lDefaultRGB[0],lDefaultRGB[1],lDefaultRGB[2]); #endif strcat(lDialogString, lTmp) ; strcat(lDialogString, " 2>&1"); } else if ( tkinter2Present( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return (char const *)1;} strcpy( lDialogString , gPython2Name ) ; if ( ! isTerminalRunning( ) && isDarwin( ) ) { strcat( lDialogString , " -i" ) ; /* for osx without console */ } strcat( lDialogString , " -S -c \"import Tkinter,tkColorChooser;root=Tkinter.Tk();root.withdraw();"); if ( isDarwin( ) ) { strcat( lDialogString , "import os;os.system('''osascript -e 'tell app \\\"Finder\\\" to set \ frontmost of process \\\"Python\\\" to true' ''');"); } strcat( lDialogString , "res=tkColorChooser.askcolor(color='" ) ; strcat(lDialogString, lpDefaultHexRGB ) ; strcat(lDialogString, "'") ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, ",title='") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "'") ; } strcat( lDialogString , ");\ \nif res[1] is not None:\n\tprint res[1]\"" ) ; } else if ( tkinter3Present( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return (char const *)1;} strcpy( lDialogString , gPython3Name ) ; strcat( lDialogString , " -S -c \"import tkinter;from tkinter import colorchooser;root=tkinter.Tk();root.withdraw();"); strcat( lDialogString , "res=colorchooser.askcolor(color='" ) ; strcat(lDialogString, lpDefaultHexRGB ) ; strcat(lDialogString, "'") ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, ",title='") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "'") ; } strcat( lDialogString , ");\ \nif res[1] is not None:\n\tprint(res[1])\"" ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){return tinyfd_inputBox(aTitle,NULL,NULL);} p = tinyfd_inputBox(aTitle, "Enter hex rgb color (i.e. #f5ca20)",lpDefaultHexRGB); if ( !p || (strlen(p) != 7) || (p[0] != '#') ) { return NULL ; } for ( i = 1 ; i < 7 ; i ++ ) { if ( ! isxdigit( p[i] ) ) { return NULL ; } } Hex2RGB(p,aoResultRGB); return p ; } if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ; if ( ! ( lIn = popen( lDialogString , "r" ) ) ) { return NULL ; } while ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) { } pclose( lIn ) ; if ( ! strlen( lBuff ) ) { return NULL ; } /* printf( "len Buff: %lu\n" , strlen(lBuff) ) ; */ /* printf( "lBuff0: %s\n" , lBuff ) ; */ if ( lBuff[strlen( lBuff ) -1] == '\n' ) { lBuff[strlen( lBuff ) -1] = '\0' ; } if ( lWasZenity3 ) { if ( lBuff[0] == '#' ) { if ( strlen(lBuff)>7 ) { lBuff[3]=lBuff[5]; lBuff[4]=lBuff[6]; lBuff[5]=lBuff[9]; lBuff[6]=lBuff[10]; lBuff[7]='\0'; } Hex2RGB(lBuff,aoResultRGB); } else if ( lBuff[3] == '(' ) { #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L sscanf(lBuff,"rgb(%hhu,%hhu,%hhu", & aoResultRGB[0], & aoResultRGB[1],& aoResultRGB[2]); #else aoResultRGB[0] = strtol(lBuff+4, & lTmp2, 10 ); aoResultRGB[1] = strtol(lTmp2+1, & lTmp2, 10 ); aoResultRGB[2] = strtol(lTmp2+1, NULL, 10 ); #endif RGB2Hex(aoResultRGB,lBuff); } else if ( lBuff[4] == '(' ) { #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L sscanf(lBuff,"rgba(%hhu,%hhu,%hhu", & aoResultRGB[0], & aoResultRGB[1],& aoResultRGB[2]); #else aoResultRGB[0] = strtol(lBuff+5, & lTmp2, 10 ); aoResultRGB[1] = strtol(lTmp2+1, & lTmp2, 10 ); aoResultRGB[2] = strtol(lTmp2+1, NULL, 10 ); #endif RGB2Hex(aoResultRGB,lBuff); } } else if ( lWasOsascript || lWasXdialog ) { /* printf( "lBuff: %s\n" , lBuff ) ; */ #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L sscanf(lBuff,"%hhu %hhu %hhu", & aoResultRGB[0], & aoResultRGB[1],& aoResultRGB[2]); #else aoResultRGB[0] = strtol(lBuff, & lTmp2, 10 ); aoResultRGB[1] = strtol(lTmp2+1, & lTmp2, 10 ); aoResultRGB[2] = strtol(lTmp2+1, NULL, 10 ); #endif RGB2Hex(aoResultRGB,lBuff); } else { Hex2RGB(lBuff,aoResultRGB); } /* printf("%d %d %d\n", aoResultRGB[0],aoResultRGB[1],aoResultRGB[2]); */ /* printf( "lBuff: %s\n" , lBuff ) ; */ return lBuff ; } /* not cross platform - zenity only */ /* contributed by Attila Dusnoki */ char const * tinyfd_arrayDialog( char const * const aTitle , /* "" */ int const aNumOfColumns , /* 2 */ char const * const * const aColumns , /* {"Column 1","Column 2"} */ int const aNumOfRows , /* 2 */ char const * const * const aCells ) /* {"Row1 Col1","Row1 Col2","Row2 Col1","Row2 Col2"} */ { static char lBuff [MAX_PATH_OR_CMD] ; char lDialogString [MAX_PATH_OR_CMD] ; FILE * lIn ; int i ; lBuff[0]='\0'; if ( zenityPresent() || matedialogPresent() || shellementaryPresent() || qarmaPresent() ) { if ( zenityPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char const *)1;} strcpy( lDialogString , "zenity" ) ; if ( (zenity3Present() >= 4) && !getenv("SSH_TTY") ) { strcat( lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } } else if ( matedialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char const *)1;} strcpy( lDialogString , "matedialog" ) ; } else if ( shellementaryPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return (char const *)1;} strcpy( lDialogString , "shellementary" ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char const *)1;} strcpy( lDialogString , "qarma" ) ; if ( !getenv("SSH_TTY") ) { strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } } strcat( lDialogString , " --list --print-column=ALL" ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, " --title=\"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\"") ; } if ( aColumns && (aNumOfColumns > 0) ) { for ( i = 0 ; i < aNumOfColumns ; i ++ ) { strcat( lDialogString , " --column=\"" ) ; strcat( lDialogString , aColumns [i] ) ; strcat( lDialogString , "\"" ) ; } } if ( aCells && (aNumOfRows > 0) ) { strcat( lDialogString , " " ) ; for ( i = 0 ; i < aNumOfRows*aNumOfColumns ; i ++ ) { strcat( lDialogString , "\"" ) ; strcat( lDialogString , aCells [i] ) ; strcat( lDialogString , "\" " ) ; } } } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"");return (char const *)0;} return NULL ; } if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ; if ( ! ( lIn = popen( lDialogString , "r" ) ) ) { return NULL ; } while ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) {} pclose( lIn ) ; if ( lBuff[strlen( lBuff ) -1] == '\n' ) { lBuff[strlen( lBuff ) -1] = '\0' ; } /* printf( "lBuff: %s\n" , lBuff ) ; */ if ( ! strlen( lBuff ) ) { return NULL ; } return lBuff ; } #endif /* _WIN32 */ /* int main( int argc , char * argv[] ) { char const * lTmp; char const * lTheSaveFileName; char const * lTheOpenFileName; char const * lTheSelectFolderName; char const * lTheHexColor; char const * lWillBeGraphicMode; unsigned char lRgbColor[3]; FILE * lIn; char lBuffer[1024]; char lString[1024]; char const * lFilterPatterns[2] = { "*.txt", "*.text" }; tinyfd_verbose = argc - 1; tinyfd_silent = 1; lWillBeGraphicMode = tinyfd_inputBox("tinyfd_query", NULL, NULL); strcpy(lBuffer, "v"); strcat(lBuffer, tinyfd_version); if (lWillBeGraphicMode) { strcat(lBuffer, "\ngraphic mode: "); } else { strcat(lBuffer, "\nconsole mode: "); } strcat(lBuffer, tinyfd_response); strcat(lBuffer, "\n"); strcat(lBuffer, tinyfd_needs+78); strcpy(lString, "tinyfiledialogs"); tinyfd_messageBox(lString, lBuffer, "ok", "info", 0); tinyfd_notifyPopup("the title", "the message\n\tfrom outer-space", "info"); if (lWillBeGraphicMode && !tinyfd_forceConsole) { tinyfd_forceConsole = ! tinyfd_messageBox("Hello World", "graphic dialogs [yes] / console mode [no]?", "yesno", "question", 1); } lTmp = tinyfd_inputBox( "a password box", "your password will be revealed", NULL); if (!lTmp) return 1; strcpy(lString, lTmp); lTheSaveFileName = tinyfd_saveFileDialog( "let us save this password", "passwordFile.txt", 2, lFilterPatterns, NULL); if (!lTheSaveFileName) { tinyfd_messageBox( "Error", "Save file name is NULL", "ok", "error", 1); return 1; } lIn = fopen(lTheSaveFileName, "w"); if (!lIn) { tinyfd_messageBox( "Error", "Can not open this file in write mode", "ok", "error", 1); return 1; } fputs(lString, lIn); fclose(lIn); lTheOpenFileName = tinyfd_openFileDialog( "let us read the password back", "", 2, lFilterPatterns, NULL, 0); if (!lTheOpenFileName) { tinyfd_messageBox( "Error", "Open file name is NULL", "ok", "error", 1); return 1; } lIn = fopen(lTheOpenFileName, "r"); if (!lIn) { tinyfd_messageBox( "Error", "Can not open this file in read mode", "ok", "error", 1); return(1); } lBuffer[0] = '\0'; fgets(lBuffer, sizeof(lBuffer), lIn); fclose(lIn); tinyfd_messageBox("your password is", lBuffer, "ok", "info", 1); lTheSelectFolderName = tinyfd_selectFolderDialog( "let us just select a directory", NULL); if (!lTheSelectFolderName) { tinyfd_messageBox( "Error", "Select folder name is NULL", "ok", "error", 1); return 1; } tinyfd_messageBox("The selected folder is", lTheSelectFolderName, "ok", "info", 1); lTheHexColor = tinyfd_colorChooser( "choose a nice color", "#FF0077", lRgbColor, lRgbColor); if (!lTheHexColor) { tinyfd_messageBox( "Error", "hexcolor is NULL", "ok", "error", 1); return 1; } tinyfd_messageBox("The selected hexcolor is", lTheHexColor, "ok", "info", 1); tinyfd_beep(); return 0; } */ #ifdef _MSC_VER #pragma warning(default:4996) #pragma warning(default:4100) #pragma warning(default:4706) #endif ================================================ FILE: app/src/main/cpp/isEngine/ext_lib/TinyFileDialogs/tinyfiledialogs.h ================================================ /*_________ / \ tinyfiledialogs.h v3.4.3 [Dec 8, 2019] zlib licence |tiny file| Unique header file created [November 9, 2014] | dialogs | Copyright (c) 2014 - 2018 Guillaume Vareille http://ysengrin.com \____ ___/ http://tinyfiledialogs.sourceforge.net \| git clone http://git.code.sf.net/p/tinyfiledialogs/code tinyfd ____________________________________________ | | | email: tinyfiledialogs at ysengrin.com | |____________________________________________| ________________________________________________________________________ | | | the windows only wchar_t UTF-16 prototypes are at the end of this file | |________________________________________________________________________| Please upvote my stackoverflow answer https://stackoverflow.com/a/47651444 tiny file dialogs (cross-platform C C++) InputBox PasswordBox MessageBox ColorPicker OpenFileDialog SaveFileDialog SelectFolderDialog Native dialog library for WINDOWS MAC OSX GTK+ QT CONSOLE & more SSH supported via automatic switch to console mode or X11 forwarding one C file + a header (add them to your C or C++ project) with 8 functions: - beep - notify popup (tray) - message & question - input & password - save file - open file(s) - select folder - color picker Complements OpenGL Vulkan GLFW GLUT GLUI VTK SFML TGUI SDL Ogre Unity3d ION OpenCV CEGUI MathGL GLM CPW GLOW Open3D IMGUI MyGUI GLT NGL STB & GUI less programs NO INIT NO MAIN LOOP NO LINKING NO INCLUDE The dialogs can be forced into console mode Windows (XP to 10) ASCII MBCS UTF-8 UTF-16 - native code & vbs create the graphic dialogs - enhanced console mode can use dialog.exe from http://andrear.altervista.org/home/cdialog.php - basic console input Unix (command line calls) ASCII UTF-8 - applescript, kdialog, zenity - python (2 or 3) + tkinter + python-dbus (optional) - dialog (opens a console if needed) - basic console input The same executable can run across desktops & distributions C89 & C++98 compliant: tested with C & C++ compilers VisualStudio MinGW-gcc GCC Clang TinyCC OpenWatcom-v2 BorlandC SunCC ZapCC on Windows Mac Linux Bsd Solaris Minix Raspbian using Gnome Kde Enlightenment Mate Cinnamon Budgie Unity Lxde Lxqt Xfce WindowMaker IceWm Cde Jds OpenBox Awesome Jwm Xdm Bindings for LUA and C# dll, Haskell Included in LWJGL(java), Rust, Allegrobasic - License - This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef TINYFILEDIALOGS_H #define TINYFILEDIALOGS_H /* #define TINYFD_NOLIB */ /* On windows, define TINYFD_NOLIB here if you don't want to include the code creating the graphic dialogs. Then you won't need to link against Comdlg32.lib and Ole32.lib */ /* if tinydialogs.c is compiled as C++ code rather than C code, you may need to comment out: extern "C" { and the corresponding closing bracket near the end of this file: } */ #ifdef __cplusplus extern "C" { #endif extern char const tinyfd_version[8]; /* contains tinyfd current version number */ extern char const tinyfd_needs[]; /* info about requirements */ extern int tinyfd_verbose; /* 0 (default) or 1 : on unix, prints the command line calls */ extern int tinyfd_silent; /* 1 (default) or 0 : on unix, hide errors and warnings from called dialog*/ #ifdef _WIN32 /* for UTF-16 use the functions at the end of this files */ extern int tinyfd_winUtf8; /* 0 (default MBCS) or 1 (UTF-8)*/ /* on windows string char can be 0:MBCS or 1:UTF-8 unless your code is really prepared for UTF-8 on windows, leave this on MBSC. Or you can use the UTF-16 (wchar) prototypes at the end of ths file.*/ #endif extern int tinyfd_forceConsole; /* 0 (default) or 1 */ /* for unix & windows: 0 (graphic mode) or 1 (console mode). 0: try to use a graphic solution, if it fails then it uses console mode. 1: forces all dialogs into console mode even when an X server is present, if the package dialog (and a console is present) or dialog.exe is installed. on windows it only make sense for console applications */ extern char tinyfd_response[1024]; /* if you pass "tinyfd_query" as aTitle, the functions will not display the dialogs but will return 0 for console mode, 1 for graphic mode. tinyfd_response is then filled with the retain solution. possible values for tinyfd_response are (all lowercase) for graphic mode: windows_wchar windows applescript kdialog zenity zenity3 matedialog qarma python2-tkinter python3-tkinter python-dbus perl-dbus gxmessage gmessage xmessage xdialog gdialog for console mode: dialog whiptail basicinput no_solution */ void tinyfd_beep(void); int tinyfd_notifyPopup( char const * const aTitle, /* NULL or "" */ char const * const aMessage, /* NULL or "" may contain \n \t */ char const * const aIconType); /* "info" "warning" "error" */ /* return has only meaning for tinyfd_query */ int tinyfd_messageBox( char const * const aTitle , /* NULL or "" */ char const * const aMessage , /* NULL or "" may contain \n \t */ char const * const aDialogType , /* "ok" "okcancel" "yesno" "yesnocancel" */ char const * const aIconType , /* "info" "warning" "error" "question" */ int const aDefaultButton ) ; /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */ char const * tinyfd_inputBox( char const * const aTitle , /* NULL or "" */ char const * const aMessage , /* NULL or "" may NOT contain \n \t on windows */ char const * const aDefaultInput ) ; /* "" , if NULL it's a passwordBox */ /* returns NULL on cancel */ char const * tinyfd_saveFileDialog( char const * const aTitle , /* NULL or "" */ char const * const aDefaultPathAndFile , /* NULL or "" */ int const aNumOfFilterPatterns , /* 0 */ char const * const * const aFilterPatterns , /* NULL | {"*.jpg","*.png"} */ char const * const aSingleFilterDescription ) ; /* NULL | "text files" */ /* returns NULL on cancel */ char const * tinyfd_openFileDialog( char const * const aTitle , /* NULL or "" */ char const * const aDefaultPathAndFile , /* NULL or "" */ int const aNumOfFilterPatterns , /* 0 */ char const * const * const aFilterPatterns , /* NULL | {"*.jpg","*.png"} */ char const * const aSingleFilterDescription , /* NULL | "image files" */ int const aAllowMultipleSelects ) ; /* 0 or 1 */ /* in case of multiple files, the separator is | */ /* returns NULL on cancel */ char const * tinyfd_selectFolderDialog( char const * const aTitle , /* NULL or "" */ char const * const aDefaultPath ) ; /* NULL or "" */ /* returns NULL on cancel */ char const * tinyfd_colorChooser( char const * const aTitle , /* NULL or "" */ char const * const aDefaultHexRGB , /* NULL or "#FF0000" */ unsigned char const aDefaultRGB[3] , /* { 0 , 255 , 255 } */ unsigned char aoResultRGB[3] ) ; /* { 0 , 0 , 0 } */ /* returns the hexcolor as a string "#FF0000" */ /* aoResultRGB also contains the result */ /* aDefaultRGB is used only if aDefaultHexRGB is NULL */ /* aDefaultRGB and aoResultRGB can be the same array */ /* returns NULL on cancel */ /************ NOT CROSS PLATFORM SECTION STARTS HERE ************************/ #ifdef _WIN32 #ifndef TINYFD_NOLIB /* windows only - utf-16 version */ int tinyfd_notifyPopupW( wchar_t const * const aTitle, /* NULL or L"" */ wchar_t const * const aMessage, /* NULL or L"" may contain \n \t */ wchar_t const * const aIconType); /* L"info" L"warning" L"error" */ /* windows only - utf-16 version */ int tinyfd_messageBoxW( wchar_t const * const aTitle , /* NULL or L"" */ wchar_t const * const aMessage, /* NULL or L"" may contain \n \t */ wchar_t const * const aDialogType, /* L"ok" L"okcancel" L"yesno" */ wchar_t const * const aIconType, /* L"info" L"warning" L"error" L"question" */ int const aDefaultButton ); /* 0 for cancel/no , 1 for ok/yes */ /* returns 0 for cancel/no , 1 for ok/yes */ /* windows only - utf-16 version */ wchar_t const * tinyfd_inputBoxW( wchar_t const * const aTitle, /* NULL or L"" */ wchar_t const * const aMessage, /* NULL or L"" may NOT contain \n nor \t */ wchar_t const * const aDefaultInput ); /* L"" , if NULL it's a passwordBox */ /* windows only - utf-16 version */ wchar_t const * tinyfd_saveFileDialogW( wchar_t const * const aTitle, /* NULL or L"" */ wchar_t const * const aDefaultPathAndFile, /* NULL or L"" */ int const aNumOfFilterPatterns, /* 0 */ wchar_t const * const * const aFilterPatterns, /* NULL or {L"*.jpg",L"*.png"} */ wchar_t const * const aSingleFilterDescription); /* NULL or L"image files" */ /* returns NULL on cancel */ /* windows only - utf-16 version */ wchar_t const * tinyfd_openFileDialogW( wchar_t const * const aTitle, /* NULL or L"" */ wchar_t const * const aDefaultPathAndFile, /* NULL or L"" */ int const aNumOfFilterPatterns , /* 0 */ wchar_t const * const * const aFilterPatterns, /* NULL {L"*.jpg",L"*.png"} */ wchar_t const * const aSingleFilterDescription, /* NULL or L"image files" */ int const aAllowMultipleSelects ) ; /* 0 or 1 */ /* in case of multiple files, the separator is | */ /* returns NULL on cancel */ /* windows only - utf-16 version */ wchar_t const * tinyfd_selectFolderDialogW( wchar_t const * const aTitle, /* NULL or L"" */ wchar_t const * const aDefaultPath); /* NULL or L"" */ /* returns NULL on cancel */ /* windows only - utf-16 version */ wchar_t const * tinyfd_colorChooserW( wchar_t const * const aTitle, /* NULL or L"" */ wchar_t const * const aDefaultHexRGB, /* NULL or L"#FF0000" */ unsigned char const aDefaultRGB[3] , /* { 0 , 255 , 255 } */ unsigned char aoResultRGB[3] ) ; /* { 0 , 0 , 0 } */ /* returns the hexcolor as a string L"#FF0000" */ /* aoResultRGB also contains the result */ /* aDefaultRGB is used only if aDefaultHexRGB is NULL */ /* aDefaultRGB and aoResultRGB can be the same array */ /* returns NULL on cancel */ #endif /*TINYFD_NOLIB*/ #else /*_WIN32*/ /* unix zenity only */ char const * tinyfd_arrayDialog( char const * const aTitle , /* NULL or "" */ int const aNumOfColumns , /* 2 */ char const * const * const aColumns, /* {"Column 1","Column 2"} */ int const aNumOfRows, /* 2 */ char const * const * const aCells); /* {"Row1 Col1","Row1 Col2","Row2 Col1","Row2 Col2"} */ #endif /*_WIN32 */ #ifdef __cplusplus } #endif #endif /* TINYFILEDIALOGS_H */ /* - This is not for android nor ios. - The code is pure C, perfectly compatible with C++. - the windows only wchar_t (utf-16) prototypes are in the header file - windows is fully supported from XP to 10 (maybe even older versions) - C# & LUA via dll, see example files - OSX supported from 10.4 to latest (maybe even older versions) - Avoid using " and ' in titles and messages. - There's one file filter only, it may contain several patterns. - If no filter description is provided, the list of patterns will become the description. - char const * filterPatterns[3] = { "*.obj" , "*.stl" , "*.dxf" } ; - On windows char defaults to MBCS, set tinyfd_winUtf8=1 to use UTF-8 - On windows link against Comdlg32.lib and Ole32.lib This linking is not compulsary for console mode (see above). - On unix: it tries command line calls, so no such need. - On unix you need one of the following: applescript, kdialog, zenity, matedialog, shellementary, qarma, python (2 or 3)/tkinter/python-dbus (optional), Xdialog or dialog (opens terminal if running without console) or xterm. - One of those is already included on most (if not all) desktops. - In the absence of those it will use gdialog, gxmessage or whiptail with a textinputbox. - If nothing is found, it switches to basic console input, it opens a console if needed (requires xterm + bash). - Use windows separators on windows and unix separators on unix. - String memory is preallocated statically for all the returned values. - File and path names are tested before return, they are valid. - If you pass only a path instead of path + filename, make sure it ends with a separator. - tinyfd_forceConsole=1; at run time, forces dialogs into console mode. - On windows, console mode only make sense for console applications. - On windows, Console mode is not implemented for wchar_T UTF-16. - Mutiple selects are not allowed in console mode. - The package dialog must be installed to run in enhanced console mode. It is already installed on most unix systems. - On osx, the package dialog can be installed via http://macappstore.org/dialog or http://macports.org - On windows, for enhanced console mode, dialog.exe should be copied somewhere on your executable path. It can be found at the bottom of the following page: http://andrear.altervista.org/home/cdialog.php - If dialog is missing, it will switch to basic console input. - You can query the type of dialog that will be use (pass "tinyfd_query" as aTitle) */ ================================================ FILE: app/src/main/cpp/isEngine/system/display/GameDisplay.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef GAMEDISPLAY_H_INCLUDED #define GAMEDISPLAY_H_INCLUDED #include "../../../app_src/gamesystem_ext/GameSystemExtended.h" #if defined(IS_ENGINE_USE_SDM) #include "SDM.h" #else #include "../entity/MainObject.h" #endif #include "../sound/GSM.h" #include "../graphic/GRM.h" namespace is { class GameDisplay; sf::Vector2f getMapPixelToCoords(GameDisplay const *scene, sf::Vector2i pixelPos); ////////////////////////////////////////////////////// /// \brief Class for manage game scene /// ////////////////////////////////////////////////////// class GameDisplay #if defined(IS_ENGINE_USE_SDM) : public SDM, public GSM, public GRM #else : public GSM, public GRM #endif { public: bool m_isClosed; /* /!\ WARNING! /!\ * This constructor is no longer supported in this version of the engine. Use the one below. * * GameDisplay(sf::RenderWindow &window, sf::View &view, is::Render &surface, GameSystemExtended &gameSysExt, sf::Color bgColor); */ GameDisplay(GameSystemExtended &gameSysExt, sf::Color bgColor); virtual ~GameDisplay(); //////////////////////////////////////////////////////////// /// \brief Update scene behavior /// /// When the SDM is activated and the user does not overload /// this function the SDM takes care of calling this method to /// automatically update the objects of the scene and the /// events of the window. //////////////////////////////////////////////////////////// virtual void step() #if !defined(IS_ENGINE_USE_SDM) = 0; #else { SDMmanageScene(); // Let SDM manage the scene } #endif //////////////////////////////////////////////////////////// /// \brief Method to implement drawing code /// /// When the SDM is activated and the user does not overload /// this function the SDM takes care of calling this method to /// automatically draw the objects of the scene. //////////////////////////////////////////////////////////// virtual void draw() #if !defined(IS_ENGINE_USE_SDM) = 0; #else { SDMdraw(); // Let SDM manage the display of objects } #endif /// Draw scene virtual void drawScreen(); /// Draw temporal loading (simulation) virtual void showTempLoading(float time = 3.f * is::SECOND); /// Allows to change an option by playing a sound and making an animation virtual void setOptionIndex(int optionIndexValue, bool callWhenClick, float buttonScale = 1.3f); /// Set option index virtual void setOptionIndex(int optionIndexValue); /// Allows to animate SFML text and sprite in relation to a option virtual void setTextAnimation(sf::Text &txt, sf::Sprite &spr, int val); /// Set sprButtonSelectScale virtual void setSprButtonSelectScale(float val); /// Update view position virtual void setView(sf::Vector2f v); /// Update view position virtual void setView(float x, float y); /// Update view position virtual void setView(); /// Set view x variable virtual void setViewVarX(float val); /// Set view y variable virtual void setViewVarY(float val); /// Set view x and y variable virtual void setViewVarXY(float x, float y); /// Set view size virtual void setViewSize(sf::Vector2f v); /// Set view size virtual void setViewSize(float x, float y); /// Set window size virtual void setWindowSize(sf::Vector2u v, bool updateViewSize = false); /// Set window title virtual void setWindowTitle(const std::string &title); /// Set background color virtual void setWindowBgColor(sf::Color color); /// Load message box resource and fonts virtual void loadParentResources(); /// Load scene resources virtual void loadResources() = 0; /// Set is running virtual void setIsRunning(bool val); /// Set is playing virtual void setIsPlaying(bool val); /// Allows access to another scene. If no scene is entered the application stops. virtual void quitScene(int nextScene = -1); /// Set wait time virtual void setWaitTime(int val); /// Set scene start virtual void setSceneStart(bool val); /// Set scene end virtual void setSceneEnd(bool val); /// Set key back (CANCEL) state virtual void setKeyBackPressed(bool val); /// Set mouse in collision virtual void setMouseInCollision(); /// Check if scene is running virtual bool getIsRunning() const; /// Return m_isPlaying virtual bool getIsPlaying() const {return m_isPlaying;} /// Return scene start virtual bool getSceneStart() const {return m_sceneStart;} /// Return scene end virtual bool getSceneEnd() const {return m_sceneEnd;} /// Return window focus state virtual bool getWindowIsActive() const {return m_windowIsActive;} /// Return key back (CANCEL) state virtual bool getKeyBackPressed() const {return m_keyBackPressed;} /// Return scene view virtual const sf::View& getView() const noexcept {return m_view;} /// Return current mouse position virtual sf::Vector2f& getMousePosCurrent() {return m_mousePosCurrent;} /// Return previous mouse position virtual sf::Vector2f& getMousePosPrevious() {return m_mousePosPrevious;} /// Return render window virtual sf::RenderWindow& getRenderWindow() const {return m_window;} /// Return render texture virtual is::Render& getRenderTexture() const {return m_surface;} /// Return game system controller virtual GameSystemExtended& getGameSystem() {return m_gameSysExt;} /// Return font system virtual sf::Font& getFontSystem() {return m_gameSysExt.GRMgetFont("font_system");} /// Return font msg virtual sf::Font& getFontMsg() {return m_gameSysExt.GRMgetFont("font_msg");} /// Return Button Select sprite virtual sf::Sprite& getSprButtonSelect() {return m_sprButtonSelect;} /// Return option index virtual int getOptionIndex() const {return m_optionIndex;} /// Return wait time virtual int getWaitTime() const {return m_waitTime;} /// Return scene width virtual unsigned int getSceneWidth() const {return m_sceneWidth;} /// Return scene height virtual unsigned int getSceneHeight() const {return m_sceneHeight;} /// Return vibrate time duration virtual short getVibrateTimeDuration() const {return m_timeVibrateDuration;} /// Return the delta time elapsed independent of the main rendering loop virtual float getDeltaTime(); /// Return DELTA_TIME variable virtual float getDELTA_TIME() const {return DELTA_TIME;} /// Return sprButtonSelectScale variable virtual float& getSprButtonSelectScale() {return m_sprButtonSelectScale;} /// Return view X virtual float getViewX() const {return m_viewX;} /// Return view Y virtual float getViewY() const {return m_viewY;} /// Return view W virtual float getViewW() const {return m_viewW;} /// Return view H virtual float getViewH() const {return m_viewH;} /// Return Cursor Position virtual sf::Vector2f getCursor(unsigned int finger = 0) const; /// Get mouse in collision bool getMouseInCollision() {return m_mouseInCollision;} /// Check if mouse current position is equal to previous position bool getMouseCurrentEqualToPrevious(); /// Return the scene background color virtual sf::Color& getBgColor() {return m_windowBgColor;} /// Check if the scene object is in view surface virtual bool inViewRec(is::MainObject *obj, bool useTexRec = true); /// Check if the scene object is in view surface virtual bool inViewRec(is::MainObject &obj, bool useTexRec = true); ////////////////////////////////////////////////////// /// \brief Test the collision of the SFML objects which are in the /// scene with the mouse cursor on PC platform / touch on mobile /// /// \param obj SFML object with which we want to test /// \param finger Finger index (on Android) ////////////////////////////////////////////////////// template bool mouseCollision(T const &obj, unsigned int finger = 0) { return is::mouseCollision(m_window, obj, finger); } ////////////////////////////////////////////////////// /// \brief Test the collision of the SFML objects which are in the /// scene with the mouse cursor on PC platform / touch on mobile /// /// \param obj SFML object with which we want to test /// \param position Allows to get the position of the collision point /// \param finger Finger index (on Android) ////////////////////////////////////////////////////// template bool mouseCollision(T const &obj, sf::Vector2f &position, unsigned int finger = 0) { return is::mouseCollision(m_window, obj, position, finger); } #if defined(IS_ENGINE_USE_SDM) /// Allows to manage all the parts of the scene (event, update, display, dialog box) virtual void SDMmanageScene(); /// Allows to define the way in which the SDM will manage the events /// To change the mechanism override this method virtual void SDMmanageSceneEvents(); /// Allows to define how the answers of the dialog box will be handled /// To change the mechanism override this method virtual void SDMmanageSceneMsgAnswers(); /// Method to call objects events virtual void SDMcallObjectsEvents(sf::Event &event); /// Method to update scene objects virtual void SDMstep(); /// Method to draw scene objects virtual void SDMdraw(); /// Allows to create a sprite by associating a texture to it. /// It is also used to blit sprites but only works with SDL. virtual void createSprite(const std::string &spriteName, is::MainObject &obj, sf::IntRect rec, sf::Vector2f position, sf::Vector2f origin, sf::Vector2f scale = sf::Vector2f(1.f, 1.f), unsigned int alpha = 255); #endif /// Allows to play sound in container by his name if the option is activated virtual void GSMplaySound(const std::string& name) { is::GSMplaySound(name, m_GSMsound, m_gameSysExt); } /// Allows to play music in container by his name if the option is activated virtual void GSMplayMusic(const std::string& name) { is::GSMplayMusic(name, //#if !defined(__ANDROID__) m_GSMmusic //#else // m_GSMsound //#endif , m_gameSysExt); } /// Allows to use Game System font in scene virtual void GRMuseGameSystemFont() { WITH(m_gameSysExt.m_GRMfont.size()) GRMaddFontObject(m_gameSysExt.m_GRMfont[_I]); } /// Allows to use Game System texture in scene virtual void GRMuseGameSystemTexture() { WITH(m_gameSysExt.m_GRMtexture.size()) GRMaddTextureObject(m_gameSysExt.m_GRMtexture[_I]); } /// Allows to use Game System sound in scene virtual void GSMuseGameSystemSound() { WITH(m_gameSysExt.m_GSMsound.size()) GSMaddSoundObject(m_gameSysExt.m_GSMsound[_I]); } /// Allows to use Game System music in scene virtual void GSMuseGameSystemMusic() { //#if !defined(__ANDROID__) WITH(m_gameSysExt.m_GSMmusic.size()) GSMaddMusicObject(m_gameSysExt.m_GSMmusic[_I]); //#else // GSMuseGameSystemSound(); //#endif } /// Allows to use all Game System resources (Font, Texture, Sound, Music) in scene virtual void GRMuseGameSystemResources() { GRMuseGameSystemFont(); GRMuseGameSystemTexture(); GSMuseGameSystemSound(); GSMuseGameSystemMusic(); } //////////////////////////////////////////////////////////// /// These methods below have the same role as those above. /// The difference here is that their name starting with GSM /// is replaced by GRM (Game Resource Manager). //////////////////////////////////////////////////////////// /// Allows to play sound in container by his name if the option is activated virtual void GRMplaySound(const std::string& name) { GSMplaySound(name); } /// Allows to play music in container by his name if the option is activated virtual void GRMplayMusic(const std::string& name) { GSMplayMusic(name); } /// Allows to use Game System sound in scene virtual void GRMuseGameSystemSound() { GSMuseGameSystemSound(); } /// Allows to use Game System music in scene virtual void GRMuseGameSystemMusic() { GSMuseGameSystemMusic(); } //////////////////////////////////////////////////////////// /// //////////////////////////////////////////////////////////// /// Show message box according to type void showMessageBox(const std::string &msgBody, bool mbYesNo = true); /// Show message box according to type void showMessageBox(std::wstring const &msgBody, bool mbYesNo = true); /// Allows to manage focus and closing events virtual void controlEventFocusClosing(sf::Event &event); protected: /// Represent the answers return by message box enum MsgAnswer { QUIT = -1, YES = 1, NO = 0 }; /// Set Message box components position void setWidgetsPosition(); /// Set message box data void setMessageBoxData(bool mbYesNo); /// Update message box components void updateMsgBox(int sliderDirection = 0, bool rightSideValidation = false, sf::Color textDefaultColor = is::GameConfig::DEFAULT_MSG_BOX_TEXT_COLOR, sf::Color selectedTextColor = is::GameConfig::DEFAULT_MSG_BOX_SELECTED_TEXT_COLOR); /// Update time wait void updateTimeWait(); /// Show message box void drawMsgBox(); sf::RenderWindow &m_window; sf::View m_view; sf::Vector2f m_mousePosPrevious, m_mousePosCurrent; is::Render &m_surface; GameSystemExtended &m_gameSysExt; sf::Sprite m_sprButtonSelect; sf::Clock m_clock; sf::Color m_windowBgColor; short const m_timeVibrateDuration; ///< Represent the time of vibration (ms) int m_optionIndex; int m_waitTime, m_msgWaitTime; unsigned int m_sceneWidth, m_sceneHeight; float DELTA_TIME; float m_viewW, m_viewH, m_viewX, m_viewY, m_sprButtonSelectScale; MsgAnswer m_msgAnswer; bool m_isRunning; bool m_windowIsActive; bool m_isPlaying, m_sceneStart, m_sceneEnd; bool m_keyBackPressed; bool m_showMsg, m_mbYesNo, m_msgBoxMouseInCollision, m_mouseInCollision; sf::Sprite m_sprMsgBox, m_sprMsgBoxButton1, m_sprMsgBoxButton2, m_sprMsgBoxButton3; sf::Sprite m_sprLoading; sf::Text m_txtMsgBox, m_txtMsgBoxYes, m_txtMsgBoxNo, m_txtMsgBoxOK; sf::RectangleShape m_recMsgBox; }; } #endif // GAMEDISPLAY_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/display/SDM.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef SDM_H_INCLUDED #define SDM_H_INCLUDED #include "../entity/MainObject.h" #if defined(IS_ENGINE_SDL_2) #include "SDMBlitSDLSprite.h" #endif #include namespace is { //////////////////////////////////////////////////////////// /// Class that automatically updates and displays objects in /// a scene. It also allows you to manage the display depth /// of objects in a scene. //////////////////////////////////////////////////////////// class SDM { public: /// Scene objects container std::list> m_SDMsceneObjects; #if defined(IS_ENGINE_SDL_2) /// Blit sprite container std::vector> m_SDMblitSDLSprite; #endif /// Allows to get object in container by his name MainObject* SDMgetObject(const std::string& name) { // update scene objects for (std::list>::iterator it = m_SDMsceneObjects.begin(); it != m_SDMsceneObjects.end(); ++it) { if (is::instanceExist(*it)) { if (it->get()->getName() == name) { return it->get(); } } } return nullptr; } ////////////////////////////////////////////////////// /// \brief Allows to add object in SDM container /// /// \param obj object (MainObject) of the scene to add /// \param callStepFunction lets tell SDM if it can use the object's Step function /// \param callDrawFunction lets tell SDM if it can use the object's Draw function ////////////////////////////////////////////////////// template void SDMaddSceneObject(std::shared_ptr obj, bool callStepFunction = true, bool callDrawFunction = true, const std::string& name = "null", bool callEventFunction = false) { obj->m_SDMcallStep = callStepFunction; obj->m_SDMcallDraw = callDrawFunction; if (!obj->m_SDMcallEvent) obj->m_SDMcallEvent = callEventFunction; if (name != "null" && name != "") obj->setName(name); m_SDMsceneObjects.push_back(obj); if (!m_SDMObjectsStep && obj->m_SDMcallStep) m_SDMObjectsStep = true; if (!m_SDMObjectsDraw && obj->m_SDMcallDraw) m_SDMObjectsDraw = true; if (!m_SDMObjectsEvent && obj->m_SDMcallEvent) m_SDMObjectsEvent = true; m_SDMsortArray = true; } /* ////////////////////////////////////////////////////// /// \brief Allows to add SFML Sprite in SDM container /// /// \param spr SFML Sprite to add /// \param name of sprite which will be used to identify it in the container in order to be able to access it /// \param depth display depth ////////////////////////////////////////////////////// virtual void SDMaddSprite(sf::Sprite spr, const std::string& name, float x = 0.f, float y = 0.f, int depth = DepthObject::NORMAL_DEPTH) { auto obj = std::make_shared(spr, x, y); obj->setName(name); obj->setDepth(depth); m_SDMsceneObjects.push_back(obj); m_SDMObjectsDraw = true; m_SDMsortArray = true; } */ ////////////////////////////////////////////////////// /// \brief Allows to add SFML Sprite in SDM container /// /// \param tex Texture of sprite /// \param name of sprite which will be used to identify it in the container in order to be able to access it /// \param center allows you to center the Sprite in relation to its position /// \param depth display depth ////////////////////////////////////////////////////// virtual void SDMaddSprite(sf::Texture &tex, const std::string& name, float x, float y, bool center = false, int depth = DepthObject::NORMAL_DEPTH) { auto obj = std::make_shared(tex, x, y, center); obj->setName(name); obj->setDepth(depth); m_SDMsceneObjects.push_back(obj); m_SDMObjectsDraw = true; m_SDMsortArray = true; } /// change the display depth of an object by his name virtual void SDMsetObjDepth(const std::string& name, int depth) { if (auto obj = SDMgetObject(name); obj != nullptr) { obj->setDepth(depth); m_SDMsortArray = true; } else is::showLog("ERROR: Can't change depth because object <" + name + "> not found !"); } /// change the display depth of an object virtual void SDMsetObjDepth(MainObject *obj, int depth) { if (obj->getDepth() != depth) { obj->setDepth(depth); m_SDMsortArray = true; } } protected: bool m_SDMsortArray = false; bool m_SDMObjectsStep = false; bool m_SDMObjectsDraw = false; bool m_SDMObjectsEvent = true; }; } #endif // SDM_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/display/SDMBlitSDLSprite.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2022 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef SDMBLITSDLSPRITE_H_INCLUDED #define SDMBLITSDLSPRITE_H_INCLUDED #if defined(IS_ENGINE_SDL_2) namespace is { //////////////////////////////////////////////////////////// /// Class which allows to blit SDL sprites //////////////////////////////////////////////////////////// class SDMBlitSDLSprite { public: SDMBlitSDLSprite(std::string name, sf::Texture &tex): m_strTextureName(name) { m_sprBlit.setTexture(tex); } sf::Sprite m_sprBlit; std::string m_strTextureName; }; } #endif #endif // SDMBLITSDLSPRITE_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/entity/Background.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef BACKGROUND_H_INCLUDED #define BACKGROUND_H_INCLUDED #include "../display/GameDisplay.h" namespace is { //////////////////////////////////////////////////////////// /// Class for drawing a background that fills a scene and /// that we can scroll vertically and horizontally with a speed //////////////////////////////////////////////////////////// class Background : public MainObject { public: float m_xGrid = 32.f; float m_yGrid = 32.f; Background(sf::Texture &tex, float x, float y, GameDisplay *scene, float hSpeed = 0.f, float vSpeed = 0.f, bool fillHorizontal = true, bool fillVertical = true): MainObject(x ,y) { m_strName = "Background"; // object name m_hsp = hSpeed; m_vsp = vSpeed; int bgW = (fillHorizontal) ? scene->getSceneWidth() + tex.getSize().x : tex.getSize().x; int bgH = (fillVertical) ? scene->getSceneHeight() + tex.getSize().y : tex.getSize().y; is::createSprite(tex, m_sprParent, sf::IntRect(0, 0, bgW, bgH), sf::Vector2f(m_x, m_y), sf::Vector2f(0.f, 0.f), true); if (m_hsp > 0.f) { m_x -= m_xGrid; m_xStart = m_x; } if (m_vsp > 0.f) { m_y -= m_yGrid; m_yStart = m_y; } } void step(const float &DELTA_TIME) { m_x += m_hsp; m_y += m_vsp; if (m_hsp > 0.f) { if (m_x > m_xStart + m_xGrid) m_x = m_xStart; } if (m_hsp < 0.f) { if (m_x < m_xStart - m_xGrid) m_x = m_xStart; } if (m_vsp > 0.f) { if (m_y > m_yStart + m_yGrid) m_y = m_yStart; } if (m_vsp < 0.f) { if (m_y < m_yStart - m_yGrid) m_y = m_yStart; } updateSprite(); } }; } #endif // BACKGROUND_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/entity/Button.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef BUTTON_H_INCLUDED #define BUTTON_H_INCLUDED #include "../display/GameDisplay.h" namespace is { //////////////////////////////////////////////////////////// /// Class which allows to use a Button in a scene. It /// supports hover and click events. //////////////////////////////////////////////////////////// class Button : public MainObject { public: Button(sf::Texture &tex, float x, float y, const std::string &title, const std::string &name, bool center, GameDisplay *scene): MainObject(x ,y), m_scene(scene), m_isInCollision(false) { m_strName = std::string((name == "" || name == " ") ? "Button_" + is::numToStr(MainObject::instanceNumber) : name); // object name m_w = tex.getSize().x / 2; m_h = tex.getSize().y; if (!center) { m_xOffset = m_w / 2.f; m_yOffset = m_h / 2.f; } is::createSprite(tex, m_sprParent, sf::IntRect(0, 0, m_w, m_h), sf::Vector2f(m_x, m_y), sf::Vector2f(m_w / 2.f, m_h / 2.f)); is::createText(scene->getFontSystem(), m_txtTitle, title, m_x, m_y, is::GameConfig::DEFAULT_BUTTON_TEXT_COLOR, true, m_h / 2); m_SDMcallEvent = true; } Button(sf::Texture &tex, sf::Font &font, float x, float y, const std::string &title, const std::string &name, bool center, int textSize, GameDisplay *scene): MainObject(x ,y), m_scene(scene), m_isInCollision(false) { m_strName = std::string((name == "" || name == " ") ? "Button_" + is::numToStr(MainObject::instanceNumber) : name); // object name m_w = tex.getSize().x / 2; m_h = tex.getSize().y; if (!center) { m_xOffset = m_w / 2.f; m_yOffset = m_h / 2.f; } is::createSprite(tex, m_sprParent, sf::IntRect(0, 0, m_w, m_h), sf::Vector2f(m_x, m_y), sf::Vector2f(m_w / 2.f, m_h / 2.f)); is::createText(font, m_txtTitle, title, m_x, m_y, is::GameConfig::DEFAULT_BUTTON_TEXT_COLOR, true, textSize); m_SDMcallEvent = true; } /// Set the button text title virtual void setTitle(const std::string &title) { m_txtTitle.setString(title); is::centerSFMLObj(m_txtTitle); } /// Triggers when the button is clicked /// This method must be overloaded virtual void onClick() = 0; /// Triggers when the user hovers over the button /// This method must be overloaded virtual void onMouseOver() { // is::showLog("WARNING: Button::onMouseOver() method must be overloaded!"); } virtual void mouseAction(sf::Event &event) { if (m_isInCollision) { auto functionClick = [this]() { onClick(); m_imageScale = ((!is::IS_ENGINE_MOBILE_OS) ? 1.2f : 1.f); }; if (event.type == sf::Event::MouseButtonPressed) { if (event.key.code == is::GameConfig::KEY_VALIDATION_MOUSE) functionClick(); } if (is::IS_ENGINE_MOBILE_OS) { if (event.type == sf::Event::TouchEnded) { if (event.touch.finger == 0) { functionClick(); } } } } } virtual void event(sf::Event &event) { mouseAction(event); } virtual void step(const float &DELTA_TIME) { bool tempCollision(m_scene->mouseCollision(m_sprParent)); if (tempCollision && !m_isInCollision) { onMouseOver(); m_imageScale = ((!is::IS_ENGINE_MOBILE_OS) ? 1.1f : 0.92f); m_isInCollision = true; } is::setSFMLObjTexRec(m_sprParent, ((m_isInCollision) ? 1 : 0) * m_w, 0, m_w, m_h); if (!tempCollision) { m_isInCollision = false; if (is::IS_ENGINE_MOBILE_OS) m_imageScale = 1.f; } if (!is::IS_ENGINE_MOBILE_OS) is::scaleAnimation(DELTA_TIME, m_imageScale, m_sprParent); updateSprite(m_x, m_y, m_imageAngle, m_imageAlpha, m_imageScale, m_imageScale, m_xOffset, m_yOffset); is::setSFMLObjProperties(m_txtTitle, is::getSFMLObjX(m_sprParent), is::getSFMLObjY(m_sprParent) #if defined(IS_ENGINE_SFML) - is::getSFMLObjHeight(m_txtTitle) / 4.f #endif , m_imageAngle, m_imageAlpha, m_imageScale, m_imageScale); } virtual void draw(is::Render &surface) { surface.draw(m_sprParent); surface.draw(m_txtTitle); } /// Return the SFML text object virtual sf::Text &getText() { return m_txtTitle; } /// Check if mouse or finger (on Android) is in collision with the Button virtual bool getIsInCollision() const { return m_isInCollision; } protected: GameDisplay *m_scene; sf::Text m_txtTitle; bool m_isInCollision; }; } #endif // BUTTON_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/entity/Form.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef FORME_H_INCLUDED #define FORME_H_INCLUDED namespace is { //////////////////////////////////////////////////////////// /// \brief Class to manage rectangle collision /// //////////////////////////////////////////////////////////// class Rectangle { public: Rectangle(): m_left(0), m_top(0), m_right(0), m_bottom(0) {} int m_left, m_top, m_right, m_bottom; }; //////////////////////////////////////////////////////////// /// \brief Class to manage circle collision /// //////////////////////////////////////////////////////////// class Circle { public: Circle(): m_x(0.f), m_y(0.f), m_raduis(0.f) {} float m_x, m_y, m_raduis; }; } #endif // FORME_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/entity/MainObject.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef MAINOBJECT_H_INCLUDED #define MAINOBJECT_H_INCLUDED #include #include #include "Form.h" #include "parents/Name.h" #include "../function/GameFunction.h" #if defined(IS_ENGINE_USE_SDM) #include "parents/Destructible.h" #include "parents/DepthObject.h" #include "parents/Visibilty.h" #endif namespace is { //////////////////////////////////////////////////////////// /// \brief Main Class for all game play objects //////////////////////////////////////////////////////////// class MainObject : public Name #if defined(IS_ENGINE_USE_SDM) , public Destructible, public DepthObject, public is::Visibility #endif { public: explicit MainObject(); MainObject(float x, float y); MainObject(sf::Sprite &spr, float x = 0.f, float y = 0.f); MainObject(sf::Texture &tex, float x, float y, bool center = false); virtual ~MainObject(); /// Return the instance number static int instanceNumber; #if defined(IS_ENGINE_USE_SDM) /// on SDL it allows to blit sprites. /// Also prevents the object's sprite from being drawn outside the view (works on SDL and SFML). std::string m_SDMblitSprTextureName = ""; /// lets SDM know if it can call its Step method (update function) bool m_SDMcallStep = true; /// lets SDM know if it can call its Draw method bool m_SDMcallDraw = true; /// lets SDM know if it can call its Event method bool m_SDMcallEvent = false; /// Allows to update object, to overload if necessary virtual void step(const float &DELTA_TIME) { updateCollisionMask(); } /// Allows to use object event virtual void event(sf::Event &ev) { is::showLog("WARNING: MainObject event called in object <" + m_strName + ">! This method must be overloaded!"); } #endif /// Set x initial position virtual void setXStart(float x); /// Set y initial position virtual void setYStart(float y); /// Set x preview position virtual void setXPrevious(float x); /// Set y preview position virtual void setYPrevious(float y); /// Set x, y initial position virtual void setStartPosition(float x, float y); /// Set x position virtual void setX(float x); /// Set y position virtual void setY(float y); /// Move object on x axis virtual void moveX(float x); /// Move object on y axis virtual void moveY(float y); /// Set x, y position virtual void setPosition(float x, float y); /// Set x, y scale of main sprite virtual void setSpriteScale(float x, float y); /// Set speed virtual void setSpeed(float val); /// Set horizontal speed virtual void setHsp(float val); /// Set vertical speed virtual void setVsp(float val); /// Set angular move virtual void setAngularMove(const float &DELTA_TIME, float speed, float angle); /// Set image frame virtual void setFrame(float val); /// Set image frame start virtual void setFrameStart(float val); /// Set image frame end virtual void setFrameEnd(float val); /// Set the x scale virtual void setImageXscale(float val); /// Set the y scale virtual void setImageYscale(float val); /// Set the scale virtual void setImageScale(float val, bool updateXYscale = false); /// Set the angle virtual void setImageAngle(float val); /// Set the x offset virtual void setXOffset(float val); /// Set the y offset virtual void setYOffset(float val); /// Set the x, y offset virtual void setXYOffset(float x, float y); /// Set the x, y offset in relation to parent sprite origin virtual void setXYOffset(); /// Set image scale virtual void setImageScaleX_Y(float x, float y); /// Set time virtual void setTime(float x); /// Set alpha virtual void setImageAlpha(int val); /// Set image index virtual void setImageIndex(int val); /// Set mask width virtual void setMaskW(int val); /// Set mask height virtual void setMaskH(int val); /// Set the size of the Rectangle mask and use it as the object's default mask virtual void setRectangleMask(int width, int height); /// Set the radius of the Circle mask and use it as the object's default mask virtual void setCircleMask(float raduis); /// Set active virtual void setIsActive(bool val); /// Update the collision mask virtual void updateCollisionMask(); /// Update the collision mask with independent x, y point virtual void updateCollisionMask(int x, int y); /// Update the collision mask by centering it in relation to the position of the object virtual void centerCollisionMask(int x, int y); /// Update object main sprite virtual void updateSprite(); /// Update object main sprite with external parameter virtual void updateSprite(float x, float y, float angle = 0.f, int alpha = 255, float xScale = 1.f, float yScale = 1.f, float xOffset = 0.f, float yOffset = 0.f); /// Draw the main sprite of object virtual void draw(is::Render &surface); /// Draw the collision mask virtual void drawMask(is::Render &surface, sf::Color color = sf::Color::Blue); /// Return the rectangle (default) mask virtual const Rectangle& getMask() const noexcept {return m_aabb;} /// Return the circle mask virtual const Circle& getCircleMask() const noexcept {return m_circle;} /// Return the x position virtual float getX() const; /// Return the y position virtual float getY() const; /// Return the x initial position virtual float getXStart() const; /// Return the y initial position virtual float getYStart() const; /// Return the x previous position virtual float getXPrevious() const; /// Return the y previous position virtual float getYPrevious() const; /// Return the distance between this object and point (x, y) virtual float distantToPoint(float x, float y) const; /// Return the distance between this object and another virtual float distantToObject(MainObject const *other, bool useSpritePosition) const; /// Return the distance between this object and another virtual float distantToObject(std::shared_ptr const &other, bool useSpritePosition) const; /// Return the angle between this object and point (x, y) virtual float pointDirection(float x, float y) const; /// Return the angle between this object and another virtual float pointDirection(std::shared_ptr const &other) const; /// Return the angle between this object main sprite and point (x, y) virtual float pointDirectionSprite(float x, float y) const; /// Return the angle between this object main sprite and another object virtual float pointDirectionSprite(std::shared_ptr const &other) const; /// Return the speed virtual float getSpeed() const; /// Return the horizontal speed virtual float getHsp() const; /// Return the vertical speed virtual float getVsp() const; /// Return frame virtual float getFrame() const; /// Return frame start virtual float getFrameStart() const; /// Return frame end virtual float getFrameEnd() const; /// Return the x scale virtual float getImageXscale() const; /// Return the y scale virtual float getImageYscale() const; /// Return the x scale virtual float getImageScale() const; /// Return the angle virtual float getImageAngle() const; /// Return x offset virtual float getXOffset() const; /// Return y offset virtual float getYOffset() const; /// Return object timing variable virtual float getTime() const; /// Return x of main sprite virtual float getSpriteX() const; /// Return y of main sprite virtual float getSpriteY() const; /// Return the sprite texture width virtual int getTextureWidth() const; /// Return the sprite texture height virtual int getTextureHeight() const; /// Return the ID of object (instance number) virtual int getInstanceId() const; /// Return the width of collision mask virtual unsigned int getMaskW() const; /// Return the height of collision mask virtual unsigned int getMaskH() const; /// Return image alpha virtual int getImageAlpha() const; /// Return image index virtual int getImageIndex() const; /// Return the width of main sprite virtual int getSpriteWidth() const; /// Return the height of main sprite virtual int getSpriteHeight() const; /// Return the x center of main sprite virtual int getSpriteCenterX() const; /// Return the y center of main sprite virtual int getSpriteCenterY() const; /// Return the number of sub-images according to the width of the sprite virtual int getSpriteNumberSubImage(int subImageWidth) const; /// Return the active value virtual bool getIsActive() const; /// Test collision in comparison with another bool placeMetting(int x, int y, MainObject const *other); /// Test collision in comparison with another bool placeMetting(int x, int y, std::shared_ptr const &other); /// Test if object is in view rectangle (vision) bool inViewRec(sf::View const &view, bool useTexRec = true); /// Return the sprite of object virtual sf::Sprite& getSprite(); protected: /// Set frame limit virtual void setFrameLimit(float frameStart, float frameEnd = -1.f); /// Sub function of placeMeting method bool placeMettingSubFunction(float x, float y, MainObject const *other) const; /// Allows to update sprite when step function is disabled void updateSDMsprite(); float m_x, m_y, m_xStart, m_yStart, m_xPrevious, m_yPrevious; float m_speed, m_hsp, m_vsp; float m_frameStart, m_frameEnd, m_frame, m_imageXscale, m_imageYscale, m_imageScale, m_imageAngle; float m_xOffset, m_yOffset; float m_time; unsigned int m_w, m_h; int m_instanceId, m_imageAlpha, m_imageIndex; bool m_isActive, m_isSDMSprite; /// Allows to draw collision mask bool m_drawMask; bool m_centerSpr = false; is::Rectangle m_aabb; is::Circle m_circle; sf::Sprite m_sprParent; }; /// Check if instance exists template bool instanceExist(std::shared_ptr const &obj) { return (obj.get() != nullptr); } /// Check if instance exists template bool instanceExist(std::unique_ptr const &obj) { return (obj.get() != nullptr); } /// Check if instance exists template bool instanceExist(T const *obj) { return (obj != nullptr); } /// destroye instance template void instanceDestroy(T *obj) { if (is::instanceExist(obj)) { delete obj; obj = nullptr; } } /// Functor for compare the x position of objects extern float COMPARE_DISTANCE; class CompareX { public: bool operator()(std::shared_ptr const &a, std::shared_ptr const &b) const { float xA = ((is::instanceExist(a)) ? a->getX() : 0.f); float xB = ((is::instanceExist(b)) ? b->getX() : 0.f); return (xA < xB); } bool operator()(MainObject const *a, MainObject const *b) const { float xA = ((is::instanceExist(a)) ? a->getX() : 0.f); float xB = ((is::instanceExist(b)) ? b->getX() : 0.f); return (xA < xB); } }; bool operator<(const MainObject *a, const MainObject &b); bool operator<(const MainObject &b, const MainObject *a); bool operator<(std::shared_ptr const &a, const MainObject &b); bool operator<(const MainObject &b, std::shared_ptr const &a); /// Sort object array by x position template void sortObjArrayByX(std::list> &v) { std::sort(v.begin(), v.end(), is::CompareX()); } /// Sort object array by x position template void sortObjArrayByX(std::list &v) { std::sort(v.begin(), v.end(), is::CompareX()); } /// Sort object array by x position template void sortObjArrayByX(std::vector> &v) { std::sort(v.begin(), v.end(), is::CompareX()); } /// Sort object array by x position template void sortObjArrayByX(std::vector &v) { std::sort(v.begin(), v.end(), is::CompareX()); } #if defined(IS_ENGINE_USE_SDM) /// Functor for compare the depth of objects class CompareDepth { public: bool operator()(std::shared_ptr const &a, std::shared_ptr const &b) const { int depthA = ((is::instanceExist(a)) ? a->getDepth() : DepthObject::VERY_BIG_DEPTH + 1); int depthB = ((is::instanceExist(b)) ? b->getDepth() : DepthObject::VERY_BIG_DEPTH + 1); return (depthA > depthB); } }; /// Sort object array by depth template void sortObjArrayByDepth(std::list> &v) { v.sort(is::CompareDepth()); } #endif } #endif // MAINOBJECT_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/entity/parents/DepthObject.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef DepthOBJECT_H_INCLUDED #define DepthOBJECT_H_INCLUDED namespace is { //////////////////////////////////////////////////////////// /// Class for manage the depth of drawing object //////////////////////////////////////////////////////////// class DepthObject { public: enum Depth { VERY_BIG_DEPTH = 99999, BIG_DEPTH = 999, NORMAL_DEPTH = 0, SMALL_DEPTH = -999, VERY_SMALL_DEPTH = -99999, }; DepthObject(int depth) : m_depth(depth) {} virtual void setDepth(int val) { m_depth = val; } virtual int getDepth() const { return m_depth; } protected: int m_depth; }; } #endif // DepthOBJECT_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/entity/parents/Destructible.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef DESTRUCTIBLE_H_INCLUDED #define DESTRUCTIBLE_H_INCLUDED namespace is { //////////////////////////////////////////////////////////// /// Class to manage the destructible object //////////////////////////////////////////////////////////// class Destructible { public: Destructible() : m_destroy(false) {} virtual void setDestroyed() { m_destroy = true; } virtual bool isDestroyed() const { return m_destroy; } protected: bool m_destroy; }; } #endif // DESTRUCTIBLE_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/entity/parents/FilePath.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef FILEPATH_H_INCLUDED #define FILEPATH_H_INCLUDED namespace is { //////////////////////////////////////////////////////////// /// class to manage the path of a file //////////////////////////////////////////////////////////// class FilePath { public: FilePath(const std::string& filePath): m_strFilePath(filePath), m_fileIsLoaded(false) {} /// Set file path virtual void setFilePath(const std::string& filePath) {m_strFilePath = filePath;} /// Return file path virtual const std::string& getFilePath() const noexcept {return m_strFilePath;} /// Allows to know if file is loaded virtual bool getFileIsLoaded() const {return m_fileIsLoaded;} protected: std::string m_strFilePath; bool m_fileIsLoaded; }; } #endif // FILEPATH_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/entity/parents/Health.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef HEALTH_H_INCLUDED #define HEALTH_H_INCLUDED namespace is { //////////////////////////////////////////////////////////// /// Provides methods and attributes to manage the health of /// a character //////////////////////////////////////////////////////////// class Health { public: Health(int health): m_health(health), m_maxHealth(health) {} Health(int health, int maxHealth): m_health(health), m_maxHealth(maxHealth) {} virtual void setHealth(int val) { m_health = val; if (m_health < 0) m_health = 0; if (m_health > m_maxHealth) m_health = m_maxHealth; } virtual void setMaxHealth(int val) { m_maxHealth = val; if (m_maxHealth < 1) m_maxHealth = 1; } virtual void addHealth(int val = 1) { m_health += val; if (m_health < 0) m_health = 0; if (m_health > m_maxHealth) m_health = m_maxHealth; } virtual int getHealth() const { return m_health; } virtual int getMaxHealth() const { return m_maxHealth; } protected: int m_health, m_maxHealth; }; } #endif // HEALTH_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/entity/parents/HurtEffect.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef HURTEFFECT_H_INCLUDED #define HURTEFFECT_H_INCLUDED #include "../MainObject.h" namespace is { //////////////////////////////////////////////////////////// /// Provides methods to make a blind effect on a character //////////////////////////////////////////////////////////// class HurtEffect { public: HurtEffect(sf::Sprite &sprParent) : m_spr(sprParent), m_isHurtTime(0.f) {} virtual void hurtStep(const float &DELTA_TIME) { if (m_isHurtTime > 0.f) { m_isHurtTime -= is::getMSecond(DELTA_TIME); is::setSFMLObjAlpha(m_spr, is::choose(5, 25, 200, 100, 25, 150)); } else { m_isHurtTime = 0.f; is::setSFMLObjAlpha(m_spr, 255); } } virtual void setIsHurt(float duration = 100.f) { m_isHurtTime = duration; } virtual bool getIsHurt() const { return (m_isHurtTime > 0.f); } protected: sf::Sprite &m_spr; float m_isHurtTime; }; } #endif // HURTEFFECT_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/entity/parents/Name.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef NAME_H_INCLUDED #define NAME_H_INCLUDED #include namespace is { //////////////////////////////////////////////////////////// /// Class for manage the name of object //////////////////////////////////////////////////////////// class Name { public: explicit Name(const std::string& name = "Unknown"): m_strName(name) {} /// Set name virtual void setName(const std::string& name) {m_strName = name;} /// Return name virtual const std::string& getName() const noexcept {return m_strName;} protected: std::string m_strName; }; } #endif // NAME_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/entity/parents/ScorePoint.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef SCOREPOINT_H_INCLUDED #define SCOREPOINT_H_INCLUDED namespace is { //////////////////////////////////////////////////////////// /// Provides methods to assign a score point to a character //////////////////////////////////////////////////////////// class ScorePoint { public: explicit ScorePoint(int point = 0) : m_scorePoint(point) {} virtual void setScorePoint(int point) { m_scorePoint = point; } virtual int getScorePoint() const { return m_scorePoint; } protected: int m_scorePoint; }; } #endif // SCOREPOINT_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/entity/parents/Step.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef STEP_H_INCLUDED #define STEP_H_INCLUDED namespace is { //////////////////////////////////////////////////////////// /// Provides methods to manage the step of an object mechanism //////////////////////////////////////////////////////////// class Step { public: Step() : m_step(0) {} Step(int step) : m_step(step) {} virtual void setStep(int val) { m_step = val; } virtual void addStep() { m_step++; } virtual void reduceStep() { m_step--; } virtual int getStep() const { return m_step; } protected: int m_step; }; } #endif // STEP_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/entity/parents/Type.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef TYPE_H_INCLUDED #define TYPE_H_INCLUDED namespace is { //////////////////////////////////////////////////////////// /// Class which allows to manage object types based on an /// enum list //////////////////////////////////////////////////////////// class Type { public: Type(int type): m_type(type) {} virtual void setType(int type) { m_type = type; } virtual int getType() const { return m_type; } protected: int m_type; }; } #endif // TYPE_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/entity/parents/Visibilty.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef VISIBILTY_H_INCLUDED #define VISIBILTY_H_INCLUDED namespace is { //////////////////////////////////////////////////////////// /// Class for manage the visibility of object //////////////////////////////////////////////////////////// class Visibility { public: explicit Visibility(bool defaultVisibility = true) : m_visible(defaultVisibility) {} virtual void setVisible(bool value) { m_visible = value; } virtual bool getVisible() const { return m_visible; } protected: bool m_visible; }; } #endif // VISIBILTY_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/function/GameFunction.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef GAME_FONCTION_H_INCLUDED #define GAME_FONCTION_H_INCLUDED #include #include #include #include #include #include "../entity/Form.h" #include "../../../app_src/config/GameConfig.h" /// Allows to browse object container (std::vector, ...) #define WITH(_SIZE) for(unsigned int _I = 0; _I < _SIZE; ++_I) #if defined(__ANDROID__) // These headers are only needed for direct NDK/JDK interaction #include #include #include #if !defined(IS_ENGINE_SDL_2) // Since we want to get the native activity from SFML, we'll have to use an // extra header here: #include #endif #endif namespace is { //////////////////////////////////////////////////////////// // Do not touch these variables unless you know what you are doing extern const float MAX_CLOCK_TIME; ///< game execution timing variables extern const float VALUE_CONVERSION; ///< game execution timing variables extern const float SECOND; ///< represent third value in second extern const float VALUE_TIME; ///< game execution timing variables //////////////////////////////////////////////////////////// static const float PI(3.14159f); //////////////////////////////////////////////////////////// /// \brief SFML Sound or Music state /// //////////////////////////////////////////////////////////// enum SFMLSndStatus { Stopped, Playing, Paused }; /// Convert number to string template int enumToNum(T val) { return static_cast(val); } /// Convert wchart_t to string std::string w_chart_tToStr(wchar_t const *str); /// Convert string to wstring std::wstring strToWStr(const std::string &str); /// Convert number to string template std::string numToStr(T val) { std::ostringstream s; s << val; return s.str(); } /// Convert string to number template T strToNum(const std::string &str) { T val; std::istringstream iss(str); iss >> val; return val; } /// Convert number to wstring template std::wstring numToWStr(T val) { std::wostringstream ws; ws << val; const std::wstring s(ws.str()); return s; } /// Draw zero behind a number template std::string writeZero(T val, int zeroNumber = 1) { std::string str; for (int i(0); i < zeroNumber; i++) if (val < std::pow(10, (i + 1))) str += "0"; return (str + is::numToStr(val)); } /// Return game execution time in millisecond int getMSecond(const float &DELTA_TIME); /// Make a tm structure representing this date std::tm makeTime(int year, int month, int day); /// Check date limit according to system date bool checkDateLimit(int year, int mont, int day); /// Show log message void showLog(const std::string& str, bool stopApplication = false); /// Get array size template inline size_t arraySize(T (&arr)[SIZE]) { return SIZE; } /// Return a random value /// \param valNumber Number of values to test template T choose(unsigned short valNumber, T x1, T x2, T x3 = 0, T x4 = 0, T x5 = 0, T x6 = 0, T x7 = 0, T x8 = 0, T x9 = 0) { unsigned int randVal = rand() % valNumber; switch(randVal) { case 1 : return x2; break; case 2 : return x3; break; case 3 : return x4; break; case 4 : return x5; break; case 5 : return x6; break; case 6 : return x7; break; case 7 : return x8; break; case 8 : return x9; break; default : break; } return x1; } /// Return a random value inline int random(unsigned int limit) { return (rand() % limit); } /// Set variable limit template void setVarLimit(T &var, T valMin, T valMax) { if (var < valMin) var = valMin; if (var > valMax) var = valMin; } /// Test many values in comparison with a variable /// \param valNumber number of values to test bool isIn(unsigned short valNumber, const int var, int x1, int x2, int x3 = 0, int x4 = 0, int x5 = 0, int x6 = 0, int x7 = 0, int x8 = 0, int x9 = 0); /// Return if a is in [b,c] bool isBetween(float a, float b, float c); /// Return if [l1,r1] intersect [l2,r2] bool isCrossing(float l1, float r1, float l2, float r2); /// Return sign of x int sign(float x); /// Return angle between two points (x1, y1) and (x2, y2) template T pointDirection(float x1, float y1, float x2, float y2) { if (static_cast(x1) == static_cast(x2)) { if (y2 > y1) return (0.5f * PI); else return (1.5f * PI); } float result = std::atan((y2 - y1) / (x2 - x1)); if (x2 < x1) result += PI; if (result < 0.f) result += 2.f * PI; result *= 180.f / PI; return result; } /// Convert radian to grade float radToDeg(float x); /// Convert grade to radian float degToRad(float x); /// Return x component of the vector /// \param useScreenScale allows to take into account the scale of the screen during calculations float lengthDirX(float dir, float angle, bool useScreenScale = false); /// Return y component of the vector /// \param useScreenScale allows to take into account the scale of the screen during calculations float lengthDirY(float dir, float angle, bool useScreenScale = false); /// Allows to increment a variable while controlling the upper limit /// \param increaseValue Will be multiplied later by @a is::VALUE_CONVERSION template void increaseVar(const float &DELTA_TIME, T &var, T increaseValue, T varFinal, T varMax) { if (var < varMax) var += ((increaseValue * is::VALUE_CONVERSION) * DELTA_TIME); else var = varFinal; } /// Allows to decrement a variable while controlling the lower limit /// \param decreasingValue Will be multiplied later by @a is::VALUE_CONVERSION template void decreaseVar(const float &DELTA_TIME, T &var, T decreaseValue, T varFinal = 0, T varMin = 0) { if (var > varMin) var -= ((decreaseValue * is::VALUE_CONVERSION) * DELTA_TIME); else var = varFinal; } /// Test collision between two rectangles bool collisionTest(Rectangle const &a, Rectangle const &b); /// Test collision between two circles bool collisionTest(Circle const &a, Circle const &b); /// Test collision between rectangle and circle bool collisionTest(Circle const &circle, Rectangle const &rec); /// Test collision between rectangle and circle bool collisionTest(Rectangle const &rec, Circle const &circle); //////////////////////////////////////////////////////////// // WARNING !!! // Below this comment all the functions that will be defined // will be linked to the SFML library. //////////////////////////////////////////////////////////// /// Return the angle of SFML object template float getSFMLObjAngle(T &obj) { return obj.getRotation(); } /// Return the angle of SFML object template float getSFMLObjAngle(T *obj) { return obj->getRotation(); } /// Return the x scale size of SFML object template float getSFMLObjXScale(T &obj) { return obj.getScale().x; } /// Return the x scale size of SFML object template float getSFMLObjXScale(T *obj) { return obj->getScale().x; } /// Return the scale size of SFML object template float getSFMLObjYScale(T &obj) { return obj.getScale().y; } /// Return the scale size of SFML object template float getSFMLObjYScale(T *obj) { return obj->getScale().y; } /// Return the width of SFML object template float getSFMLObjWidth(T &obj) { return obj.getGlobalBounds().width; } /// Return the width of SFML object template float getSFMLObjWidth(T *obj) { return obj->getGlobalBounds().width; } /// Return the y height of SFML object template float getSFMLObjHeight(T &obj) { return obj.getGlobalBounds().height; } /// Return the y height of SFML object template float getSFMLObjHeight(T *obj) { return obj->getGlobalBounds().height; } /// Return the width of SFML texture inline int getSFMLTextureWidth(sf::Texture const &obj) { return obj.getSize().x; } /// Return the width of SFML texture inline int getSFMLTextureWidth(sf::Texture const *obj) { return obj->getSize().x; } /// Return the height of SFML texture inline int getSFMLTextureHeight(sf::Texture const &obj) { return obj.getSize().y; } /// Return the height of SFML texture inline int getSFMLTextureHeight(sf::Texture const *obj) { return obj->getSize().y; } /// Return the x origin of SFML object template float getSFMLObjOriginX(T &obj) { return obj.getOrigin().x; } /// Return the y origin of SFML object template float getSFMLObjOriginY(T &obj) { return obj.getOrigin().y; } /// Return the x position of SFML object template float getSFMLObjX(T &obj) { return obj.getPosition().x; } /// Return the x position of SFML object template float getSFMLObjX(T *obj) { return obj->getPosition().x; } /// Return the y position of SFML object template float getSFMLObjY(T &obj) { return obj.getPosition().y; } /// Return the y position of SFML object template float getSFMLObjY(T *obj) { return obj->getPosition().y; } /// Return the alpha of SFML object template unsigned int getSFMLObjAlpha(T &obj) { return obj.getColor().a; } /// Return the alpha of SFML object template unsigned int getSFMLObjAlpha(T *obj) { return obj->getColor().a; } /// Set the angle of SFML object template void setSFMLObjAngle(T &obj, float angle) { obj.setRotation(angle); } /// Set the angle of SFML object template void setSFMLObjAngle(T *obj, float angle) { obj->setRotation(angle); } /// Set rotation of SFML object template void setSFMLObjRotate(T &obj, float rotationSpeed) { obj.rotate(rotationSpeed); } /// Set rotation of SFML object template void setSFMLObjRotate(T *obj, float rotationSpeed) { obj->rotate(rotationSpeed); } /// Set the x scale of SFML object template void setSFMLObjXScale(T &obj, float x) { obj.setScale(x, obj.getScale().y); } /// Set the x scale of SFML object template void setSFMLObjXScale(T *obj, float x) { obj->setScale(x, obj->getScale().y); } /// Set the y scale of SFML object template void setSFMLObjYScale(T &obj, float y) { obj.setScale(obj.getScale().x, y); } /// Set the y scale of SFML object template void setSFMLObjYScale(T *obj, float y) { obj->setScale(obj->getScale().x, y); } /// Set the x, y scale of SFML object template void setSFMLObjScaleX_Y(T &obj, float x, float y) { obj.setScale(x, y); } /// Set the x, y scale of SFML object template void setSFMLObjScaleX_Y(T *obj, float x, float y) { obj->setScale(x, y); } /// Set the scale of SFML object template void setSFMLObjScale(T &obj, float scale) { obj.setScale(scale, scale); } /// Set the scale of SFML object template void setSFMLObjScale(T *obj, float scale) { obj->setScale(scale, scale); } /// Set origin of SFML object template void setSFMLObjOrigin(T &obj, float x, float y) { obj.setOrigin(x, y); } /// Set origin of SFML object template void setSFMLObjOrigin(T *obj, float x, float y) { obj->setOrigin(x, y); } /// Set x position of SFML object template void setSFMLObjX(T &obj, float x) { obj.setPosition(x, obj.getPosition().y); } /// Set x position of SFML object template void setSFMLObjX(T *obj, float x) { obj->setPosition(x, obj->getPosition().y); } /// Set y position of SFML object template void setSFMLObjY(T &obj, float y) { obj.setPosition(obj.getPosition().x, y); } /// Set y position of SFML object template void setSFMLObjY(T *obj, float y) { obj->setPosition(obj->getPosition().x, y); } /// Set x, y position of SFML object template void setSFMLObjX_Y(T &obj, float x, float y) { obj.setPosition(x, y); } /// Set x, y position of SFML object template void setSFMLObjX_Y(T *obj, float x, float y) { obj->setPosition(x, y); } /// Set x, y position of SFML object template void setSFMLObjX_Y(T &obj, sf::Vector2f position) { obj.setPosition(position.x, position.y); } /// Set x, y position of SFML object template void setSFMLObjX_Y(T *obj, sf::Vector2f position) { obj->setPosition(position.x, position.y); } /// Move SFML object on x axis template void moveSFMLObjX(T &obj, float speed) { obj.setPosition(obj.getPosition().x + speed, obj.getPosition().y); } /// Move SFML object on x axis template void moveSFMLObjX(T *obj, float speed) { obj->setPosition(obj->getPosition().x + speed, obj->getPosition().y); } /// Move SFML object on y axis template void moveSFMLObjY(T &obj, float speed) { obj.setPosition(obj.getPosition().x, obj.getPosition().y + speed); } /// Move SFML object on y axis template void moveSFMLObjY(T *obj, float speed) { obj->setPosition(obj->getPosition().x, obj->getPosition().y + speed); } /// Set SFML object size template void setSFMLObjSize(T &obj, float x, float y) { obj.setSize(sf::Vector2f(x, y)); } /// Set SFML object size template void setSFMLObjSize(T *obj, float x, float y) { obj->setSize(sf::Vector2f(x, y)); } /// Set SFML object size template void setSFMLObjSize(T &obj, sf::Vector2f v) { setSFMLObjSize(obj, v.x, v.y); } /// Set SFML object size template void setSFMLObjSize(T *obj, sf::Vector2f v) { setSFMLObjSize(obj, v.x, v.y); } /// Set the alpha of SFML object template void setSFMLObjAlpha(T &obj, unsigned int alpha) { obj.setColor(sf::Color(obj.getColor().r, obj.getColor().g, obj.getColor().b, alpha)); } /// Set the alpha of SFML object template void setSFMLObjAlpha(T *obj, unsigned int alpha) { obj->setColor(sf::Color(obj->getColor().r, obj->getColor().g, obj->getColor().b, alpha)); } /// Set the alpha of SFML shape or text object template void setSFMLObjAlpha2(T &obj, unsigned int alpha) { obj.setFillColor(sf::Color(obj.getFillColor().r, obj.getFillColor().g, obj.getFillColor().b, alpha)); } /// Set the alpha of SFML shape or text object template void setSFMLObjAlpha2(T *obj, unsigned int alpha) { obj->setFillColor(sf::Color(obj->getFillColor().r, obj->getFillColor().g, obj->getFillColor().b, alpha)); } /// Set the alpha and uniform RGB color of SFML object template void setSFMLObjAlpha(T &obj, unsigned int alpha, sf::Uint8 rgb) { obj.setColor(sf::Color(rgb, rgb, rgb, alpha)); } /// Set the alpha and uniform RGB color of SFML object template void setSFMLObjAlpha(T *obj, unsigned int alpha, sf::Uint8 rgb) { obj->setColor(sf::Color(rgb, rgb, rgb, alpha)); } /// Set the alpha and RGB distinct color of SFML object template void setSFMLObjAlpha(T &obj, unsigned int alpha, int r, int g, int b) { obj.setColor(sf::Color(r, g, b, alpha)); } /// Set the alpha and RGB distinct color of SFML object template void setSFMLObjAlpha(T *obj, unsigned int alpha, int r, int g, int b) { obj->setColor(sf::Color(r, g, b, alpha)); } /// Set the color of SFML object template void setSFMLObjColor(T &obj, sf::Color color) { obj.setColor(color); } /// Set the color of SFML object template void setSFMLObjColor(T *obj, sf::Color color) { obj->setColor(color); } /// Set the color of SFML shape template void setSFMLObjFillColor(T &obj, sf::Color color) { obj.setFillColor(color); } /// Set the color of SFML shape template void setSFMLObjFillColor(T *obj, sf::Color color) { obj->setFillColor(color); } /// Allows to make scale animation template void scaleAnimation(const float &DELTA_TIME, float &var, T &obj, short varSign = 1, float scaleSize = 1.f) { if (var > scaleSize) var -= ((0.05f * is::VALUE_CONVERSION) * DELTA_TIME); else var = scaleSize; setSFMLObjScale(obj, varSign * var); } /// Allows to make scale animation template void scaleAnimation(const float &DELTA_TIME, float &var, T *obj, short varSign = 1, float scaleSize = 1.f) { scaleAnimation(DELTA_TIME, var, *obj, varSign, scaleSize); } /// Allows to animate SFML text in relation to a option void setTextAnimation(sf::Text &txt, int &var, int val); /// Allows to animate SFML text in relation to a option inline void setTextAnimation(sf::Text *txt, int &var, int val) { setTextAnimation(*txt, var, val); } /// Allows to animate SFML text and sprites in relation to a option void setTextAnimation(sf::Text &txt, sf::Sprite &spr, sf::Sprite &sprSelected, int &var, int val); /// Allows to animate SFML text and sprites in relation to a option inline void setTextAnimation(sf::Text *txt, sf::Sprite *spr, sf::Sprite *sprSelected, int &var, int val) { setTextAnimation(*txt, *spr, *sprSelected, var, val); } /// Set the sprite frame with different size (e.g 64x32) void setFrame(sf::Sprite &sprite, float frame, int subFrame, int frameWidth, int frameHeight, int recWidth, int recHeight); /// Set the sprite frame with different size (e.g 64x32) inline void setFrame(sf::Sprite *sprite, float frame, int subFrame, int frameWidth, int frameHeight, int recWidth, int recHeight) { setFrame(*sprite, frame, subFrame, frameWidth, frameHeight, recWidth, recHeight); } /// Set the sprite frame with the same size (e.g 64x64) void setFrame(sf::Sprite &sprite, float frame, int subFrame, int frameSize); /// Set the sprite frame with the same size (e.g 64x64) inline void setFrame(sf::Sprite *sprite, float frame, int subFrame, int frameSize) { setFrame(*sprite, frame, subFrame, frameSize); } /// Set the outline thickness and color of SFML object template void setSFMLObjOutlineColor(T &obj, sf::Color color, float thickness = 1.f) { obj.setOutlineThickness(thickness); obj.setOutlineColor(color); } /// Set the outline thickness and color of SFML object template void setSFMLObjOutlineColor(T *obj, sf::Color color, float thickness = 1.f) { setSFMLObjOutlineColor(*obj, color, thickness); } /// Set Texture Rec of SFML object template void setSFMLObjTexRec(T &obj, int x, int y, int w, int h) { obj.setTextureRect(sf::IntRect(x, y, w, h)); } /// Set Texture Rec of SFML object template void setSFMLObjTexRec(T *obj, int x, int y, int w, int h) { obj->setTextureRect(sf::IntRect(x, y, w, h)); } /// Set the properties of SFML Sprite inline void setSFMLObjProperties(sf::Sprite &obj, float x, float y, float angle = 0.f, int alpha = 255, float xScale = 1.f, float yScale = 1.f) { is::setSFMLObjAlpha(obj, alpha); is::setSFMLObjAngle(obj, angle); is::setSFMLObjScaleX_Y(obj, xScale, yScale); is::setSFMLObjX_Y(obj, x, y); } /// Set the properties of SFML Sprite inline void setSFMLObjProperties(sf::Sprite *obj, float x, float y, float angle = 0.f, int alpha = 255, float xScale = 1.f, float yScale = 1.f) { setSFMLObjProperties(*obj, x, y, angle, alpha, xScale, yScale); } /// Set the properties of SFML Text inline void setSFMLObjProperties(sf::Text &obj, float x, float y, float angle = 0.f, int alpha = 255, float xScale = 1.f, float yScale = 1.f) { is::setSFMLObjAlpha2(obj, alpha); is::setSFMLObjAngle(obj, angle); is::setSFMLObjScaleX_Y(obj, xScale, yScale); is::setSFMLObjX_Y(obj, x, y); } /// Set the properties of SFML Text inline void setSFMLObjProperties(sf::Text *obj, float x, float y, float angle = 0.f, int alpha = 255, float xScale = 1.f, float yScale = 1.f) { setSFMLObjProperties(*obj, x, y, angle, alpha, xScale, yScale); } /// Set the properties of SFML Rectangle inline void setSFMLObjProperties(sf::RectangleShape &obj, float x, float y, float angle = 0.f, int alpha = 255, float xScale = 1.f, float yScale = 1.f) { is::setSFMLObjAlpha2(obj, alpha); is::setSFMLObjAngle(obj, angle); is::setSFMLObjScaleX_Y(obj, xScale, yScale); is::setSFMLObjX_Y(obj, x, y); } /// Set the properties of SFML Rectangle inline void setSFMLObjProperties(sf::RectangleShape *obj, float x, float y, float angle = 0.f, int alpha = 255, float xScale = 1.f, float yScale = 1.f) { setSFMLObjProperties(*obj, x, y, angle, alpha, xScale, yScale); } /// Set the properties of SFML Circle inline void setSFMLObjProperties(sf::CircleShape &obj, float x, float y, float angle = 0.f, int alpha = 255, float xScale = 1.f, float yScale = 1.f) { is::setSFMLObjAlpha2(obj, alpha); is::setSFMLObjAngle(obj, angle); is::setSFMLObjScaleX_Y(obj, xScale, yScale); is::setSFMLObjX_Y(obj, x, y); } /// Set the properties of SFML Circle inline void setSFMLObjProperties(sf::CircleShape *obj, float x, float y, float angle = 0.f, int alpha = 255, float xScale = 1.f, float yScale = 1.f) { setSFMLObjProperties(*obj, x, y, angle, alpha, xScale, yScale); } /// Center SFML object template void centerSFMLObj(T &obj) { obj.setOrigin( #if !defined(IS_ENGINE_SFML) obj.getTextureRect().width / 2, obj.getTextureRect().height / 2 #else obj.getGlobalBounds().width / 2, obj.getGlobalBounds().height / 2 #endif ); } /// Center SFML object template void centerSFMLObj(T *obj) { centerSFMLObj(*obj); } /// Center SFML object X template void centerSFMLObjX(T &obj) { obj.setOrigin( #if !defined(IS_ENGINE_SFML) obj.getTextureRect().width / 2 #else obj.getGlobalBounds().width / 2 #endif , obj.getOrigin().y); } /// Center SFML object X template void centerSFMLObjX(T *obj) { centerSFMLObjX(*obj); } /// Center SFML object Y template void centerSFMLObjY(T &obj) { obj.setOrigin(obj.getOrigin().x, #if !defined(IS_ENGINE_SFML) obj.getTextureRect().height / 2 #else obj.getGlobalBounds().height / 2 #endif ); } /// Center SFML object Y template void centerSFMLObjY(T *obj) { centerSFMLObjY(*obj); } /// Load SFML Texture Resource inline void loadSFMLTexture(sf::Texture &obj, const std::string& filePath) { obj.loadFromFile(filePath); } /// Load SFML Texture Resource inline void loadSFMLTexture(sf::Texture *obj, const std::string& filePath) { obj->loadFromFile(filePath); } /// Load SFML Font Resource inline void loadSFMLFont(sf::Font &obj, const std::string& filePath, int fontDefaultSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE) { #if !defined(IS_ENGINE_SFML) obj.setSDLFontSize(fontDefaultSize); #endif obj.loadFromFile(filePath); } /// Load SFML Font Resource inline void loadSFMLFont(sf::Font *obj, const std::string& filePath, int fontDefaultSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE) { loadSFMLFont(*obj, filePath, fontDefaultSize); } /// Load SFML Sound Buffer Resource inline void loadSFMLSoundBuffer(sf::SoundBuffer &obj, const std::string& filePath) { obj.loadFromFile(filePath); } /// Load SFML Sound Buffer Resource inline void loadSFMLSoundBuffer(sf::SoundBuffer *obj, const std::string& filePath) { obj->loadFromFile(filePath); } /// Load SFML Sound Buffer Resource and define it with the Sound inline void loadSFMLSoundBufferWithSnd(sf::SoundBuffer &sb, sf::Sound &snd, const std::string& filePath) { if (sb.loadFromFile(filePath)) snd.setBuffer(sb); else showLog("ERROR: Can't load Sound Buffer : " + filePath + " with sound"); } /// Load SFML Sound Buffer Resource and define it with the Sound inline void loadSFMLSoundBufferWithSnd(sf::SoundBuffer *sb, sf::Sound *snd, const std::string& filePath) { loadSFMLSoundBufferWithSnd(*sb, *snd, filePath); } /// Load SFML Music Resource inline void loadSFMLMusic(sf::Music &obj, const std::string& filePath) { obj.openFromFile(filePath); } /// Load SFML Music Resource inline void loadSFMLMusic(sf::Music *obj, const std::string& filePath) { obj->openFromFile(filePath); } /// Check SFML Sound state template bool checkSFMLSndState(T &obj, SFMLSndStatus state) { switch (state) { case SFMLSndStatus::Playing: return (obj.getStatus() == sf::Sound::Status::Playing); break; case SFMLSndStatus::Stopped: return (obj.getStatus() == sf::Sound::Status::Stopped); break; case SFMLSndStatus::Paused: return (obj.getStatus() == sf::Sound::Status::Paused); break; } return false; } /// Check SFML Sound state template bool checkSFMLSndState(T *obj, SFMLSndStatus state) { if (obj != nullptr) { switch (state) { case SFMLSndStatus::Playing: return (obj->getStatus() == sf::Sound::Status::Playing); case SFMLSndStatus::Stopped: return (obj->getStatus() == sf::Sound::Status::Stopped); case SFMLSndStatus::Paused: return (obj->getStatus() == sf::Sound::Status::Paused); } } return false; } /// Allows to play SFML Sound or Music template void playSFMLSnd(T &obj) { obj.play(); } /// Allows to play SFML Sound or Music template void playSFMLSnd(T *obj) { if (obj != nullptr) obj->play(); } /// Allows to stop SFML Sound or Music template void stopSFMLSnd(T &obj) { obj.stop(); } /// Allows to stop SFML Sound or Music template void stopSFMLSnd(T *obj) { if (obj != nullptr) obj->stop(); } /// Allows to pause SFML Sound or Music template void pauseSFMLSnd(T &obj) { obj.pause(); } /// Allows to pause SFML Sound or Music template void pauseSFMLSnd(T *obj) { if (obj != nullptr) obj->pause(); } /// Allows to set SFML Sound or Music loop template void loopSFMLSnd(T &obj, bool val) { obj.loop(val); } /// Allows to set SFML Sound or Music loop template void loopSFMLSnd(T *obj, bool val) { if (obj != nullptr) obj->loop(val); } /// Test collision between SFML object template bool collisionTestSFML(A const &objA, B const &objB) { return (objB.getGlobalBounds().intersects(objA.getGlobalBounds())); } /// Test collision between SFML object template bool collisionTestSFML(A const *objA, B const *objB) { return (objB->getGlobalBounds().intersects(objA->getGlobalBounds())); } /// Create SFML Render Texture //void createRenderTexture(sf::RenderTexture &renderTexture, unsigned int width, unsigned int height); /// Create SFML rectangle void createRectangle(sf::RectangleShape &rec, sf::Vector2f recSize, sf::Color color, float x = 0.f, float y = 0.f, bool center = true); /// Create SFML rectangle inline void createRectangle(sf::RectangleShape *rec, sf::Vector2f recSize, sf::Color color, float x = 0.f, float y = 0.f, bool center = true) { createRectangle(*rec, recSize, color, x, y, center); } /// Set SFML Text style void textStyleConfig(sf::Text &txt, bool underLined, bool boldText, bool italicText); /// Set SFML Text style inline void textStyleConfig(sf::Text *txt, bool underLined, bool boldText, bool italicText) { textStyleConfig(*txt, underLined, boldText, italicText); } /// Set SFML Text outline color void setSFMLTextOutlineColor(sf::Text &txt, float thickness, sf::Color color); /// Set SFML Text outline color inline void setSFMLTextOutlineColor(sf::Text *txt, float thickness, sf::Color color) { setSFMLTextOutlineColor(*txt, thickness, color); } /// Create SFML text template void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text &txt, T const &text, float x, float y, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { txt.setFont(fnt); if (txtSize > 0) txt.setCharacterSize(txtSize); else txt.setCharacterSize(is::GameConfig::DEFAULT_SFML_TEXT_SIZE); textStyleConfig(txt, underLined, boldText, italicText); txt.setString(text); is::setSFMLObjX_Y(txt, x, y); is::setSFMLObjFillColor(txt, is::GameConfig::DEFAULT_SFML_TEXT_COLOR); } /// Create SFML text with center parameter template void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text &txt, T const &text, float x, float y, bool centerText, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, txt, text, x, y, txtSize, underLined, boldText, italicText); if (centerText) is::centerSFMLObj(txt); is::setSFMLObjX_Y(txt, x, y); } /// Create SFML text with color and size template void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text &txt, T const &text, float x, float y, sf::Color color, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, txt, text, x, y, txtSize, underLined, boldText, italicText); is::setSFMLObjFillColor(txt, color); } /// Create SFML text with color, size and center template void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text &txt, T const &text, float x, float y, sf::Color color, bool centerText, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, txt, text, x, y, centerText, txtSize, underLined, boldText, italicText); is::setSFMLObjFillColor(txt, color); } /// Create SFML text outline with color and size template void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text &txt, T const &text, float x, float y, sf::Color color, sf::Color outlineColor, bool centerText, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, float outlineThickness = 1.f, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, txt, text, x, y, color, centerText, txtSize, underLined, boldText, italicText); txt.setOutlineColor(outlineColor); txt.setOutlineThickness(outlineThickness); } /// Create SFML text in Vector template void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, std::vector &txt, T const &text, float x, float y, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { txt.push_back(new sf::Text()); createText(fnt, *txt[txt.size() - 1], text, x, y, txtSize, underLined, boldText, italicText); } /// Create SFML text with center parameter in Vector template void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, std::vector &txt, T const &text, float x, float y, bool centerText, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { txt.push_back(new sf::Text()); createText(fnt, *txt[txt.size() - 1], text, x, y, centerText, txtSize, underLined, boldText, italicText); } /// Create SFML text with color and size in Vector template void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, std::vector &txt, T const &text, float x, float y, sf::Color color, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { txt.push_back(new sf::Text()); createText(fnt, *txt[txt.size() - 1], text, x, y, color, txtSize, underLined, boldText, italicText); } /// Create SFML text with color, size and center in Vector template void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, std::vector &txt, T const &text, float x, float y, sf::Color color, bool centerText, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { txt.push_back(new sf::Text()); createText(fnt, *txt[txt.size() - 1], text, x, y, color, centerText, txtSize, underLined, boldText, italicText); } /// Create SFML text outline with color and size in Vector template void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, std::vector &txt, T const &text, float x, float y, sf::Color color, sf::Color outlineColor, bool centerText, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, float outlineThickness = 1.f, bool underLined = false, bool boldText = false, bool italicText = false) { txt.push_back(new sf::Text()); createText(fnt, *txt[txt.size() - 1], text, x, y, color, outlineColor, centerText, txtSize, outlineThickness, underLined, boldText, italicText); } /// Create SFML text template void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text *txt, T const &text, float x, float y, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, *txt, text, x, y, txtSize, underLined, boldText, italicText); } /// Create SFML text with center parameter template void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text *txt, T const &text, float x, float y, bool centerText, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, *txt, text, x, y, centerText, txtSize, underLined, boldText, italicText); } /// Create SFML text with color and size template void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text *txt, T const &text, float x, float y, sf::Color color, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, *txt, text, x, y, color, txtSize, underLined, boldText, italicText); } /// Create SFML text with color, size and center template void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text *txt, T const &text, float x, float y, sf::Color color, bool centerText, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, *txt, text, x, y, color, centerText, txtSize, underLined, boldText, italicText); } /// Create SFML text outline with color and size template void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text *txt, T const &text, float x, float y, sf::Color color, sf::Color outlineColor, bool centerText, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, float outlineThickness = 1.f, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, *txt, text, x, y, color, outlineColor, centerText, txtSize, outlineThickness, underLined, boldText, italicText); } /// Create SFML text inline void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text &txt, const int value, float x, float y, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, txt, numToStr(value), x, y, txtSize, underLined, boldText, italicText); } /// Create SFML text with center parameter inline void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text &txt, const int value, float x, float y, bool centerText, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, txt, numToStr(value), x, y, centerText, txtSize, underLined, boldText, italicText); } /// Create SFML text with color and size inline void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text &txt, const int value, float x, float y, sf::Color color, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, txt, numToStr(value), x, y, color, txtSize, underLined, boldText, italicText); } /// Create SFML text with color, size and center inline void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text &txt, const int value, float x, float y, sf::Color color, bool centerText, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, txt, numToStr(value), x, y, color, centerText, txtSize, underLined, boldText, italicText); } /// Create SFML text outline with color and size inline void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text &txt, const int value, float x, float y, sf::Color color, sf::Color outlineColor, bool centerText, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, float outlineThickness = 1.f, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, txt, numToStr(value), x, y, color, outlineColor, centerText, txtSize, outlineThickness, underLined, boldText, italicText); } /// Create SFML text inline void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text *txt, const int value, float x, float y, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, *txt, numToStr(value), x, y, txtSize, underLined, boldText, italicText); } /// Create SFML text with center parameter inline void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text *txt, const int value, float x, float y, bool centerText, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, *txt, numToStr(value), x, y, centerText, txtSize, underLined, boldText, italicText); } /// Create SFML text with color and size inline void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text *txt, const int value, float x, float y, sf::Color color, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, *txt, numToStr(value), x, y, color, txtSize, underLined, boldText, italicText); } /// Create SFML text with color, size and center inline void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text *txt, const int value, float x, float y, sf::Color color, bool centerText, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, *txt, numToStr(value), x, y, color, centerText, txtSize, underLined, boldText, italicText); } /// Create SFML text outline with color and size inline void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text *txt, const int value, float x, float y, sf::Color color, sf::Color outlineColor, bool centerText, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, float outlineThickness = 1.f, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, *txt, numToStr(value), x, y, color, outlineColor, centerText, txtSize, outlineThickness, underLined, boldText, italicText); } /// Create SFML text inline void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text &txt, const float value, float x, float y, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, txt, numToStr(value), x, y, txtSize, underLined, boldText, italicText); } /// Create SFML text with center parameter inline void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text &txt, const float value, float x, float y, bool centerText, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, txt, numToStr(value), x, y, centerText, txtSize, underLined, boldText, italicText); } /// Create SFML text with color and size inline void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text &txt, const float value, float x, float y, sf::Color color, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, txt, numToStr(value), x, y, color, txtSize, underLined, boldText, italicText); } /// Create SFML text with color, size and center inline void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text &txt, const float value, float x, float y, sf::Color color, bool centerText, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, txt, numToStr(value), x, y, color, centerText, txtSize, underLined, boldText, italicText); } /// Create SFML text outline with color and size inline void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text &txt, const float value, float x, float y, sf::Color color, sf::Color outlineColor, bool centerText, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, float outlineThickness = 1.f, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, txt, numToStr(value), x, y, color, outlineColor, centerText, txtSize, outlineThickness, underLined, boldText, italicText); } /// Create SFML text inline void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text *txt, const float value, float x, float y, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, *txt, numToStr(value), x, y, txtSize, underLined, boldText, italicText); } /// Create SFML text with center parameter inline void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text *txt, const float value, float x, float y, bool centerText, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, *txt, numToStr(value), x, y, centerText, txtSize, underLined, boldText, italicText); } /// Create SFML text with color and size inline void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text *txt, const float value, float x, float y, sf::Color color, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, *txt, numToStr(value), x, y, color, txtSize, underLined, boldText, italicText); } /// Create SFML text with color, size and center inline void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text *txt, const float value, float x, float y, sf::Color color, bool centerText, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, *txt, numToStr(value), x, y, color, centerText, txtSize, underLined, boldText, italicText); } /// Create SFML text outline with color and size inline void createText(sf::Font #if defined(IS_ENGINE_SFML) const #endif &fnt, sf::Text *txt, const float value, float x, float y, sf::Color color, sf::Color outlineColor, bool centerText, int txtSize = is::GameConfig::DEFAULT_SFML_TEXT_SIZE, float outlineThickness = 1.f, bool underLined = false, bool boldText = false, bool italicText = false) { createText(fnt, *txt, numToStr(value), x, y, color, outlineColor, centerText, txtSize, outlineThickness, underLined, boldText, italicText); } /// Create SFML sprites without IntRec void createSprite(sf::Texture &tex, sf::Sprite &spr, sf::Vector2f position, sf::Vector2f origin, bool smooth = true); /// Create SFML sprites without IntRec inline void createSprite(sf::Texture &tex, sf::Sprite *spr, sf::Vector2f position, sf::Vector2f origin, bool smooth = true) { createSprite(tex, *spr, position, origin, smooth); } /// Create SFML sprites center without IntRec inline void createSprite(sf::Texture &tex, sf::Sprite &spr, sf::Vector2f position, bool center, bool smooth = true) { createSprite(tex, spr, position, sf::Vector2f(tex.getSize().x / 2.f, tex.getSize().y / 2.f), smooth); } /// Create SFML sprites center without IntRec inline void createSprite(sf::Texture &tex, sf::Sprite *spr, sf::Vector2f position, bool center, bool smooth = true) { createSprite(tex, *spr, position, center, smooth); } /// Create SFML sprites with IntRec void createSprite(sf::Texture &tex, sf::Sprite &spr, sf::IntRect rec, sf::Vector2f position, sf::Vector2f origin, bool repeatTexture = false, bool smooth = true); /// Create SFML sprites with IntRec inline void createSprite(sf::Texture &tex, sf::Sprite *spr, sf::IntRect rec, sf::Vector2f position, sf::Vector2f origin, bool repeatTexture = false, bool smooth = true) { createSprite(tex, *spr, rec, position, origin, repeatTexture, smooth); } /// Create SFML sprites center with IntRec inline void createSprite(sf::Texture &tex, sf::Sprite &spr, sf::IntRect rec, sf::Vector2f position, bool center, bool repeatTexture = false, bool smooth = true) { createSprite(tex, spr, rec, position, sf::Vector2f(rec.width / 2.f, rec.height / 2.f), repeatTexture, smooth); } /// Create SFML sprites center with IntRec inline void createSprite(sf::Texture &tex, sf::Sprite *spr, sf::IntRect rec, sf::Vector2f position, bool center, bool repeatTexture = false, bool smooth = true) { createSprite(tex, *spr, rec, position, center, repeatTexture, smooth); } /// Create SFML sprites advanced void createSprite(sf::Texture &tex, sf::Sprite &spr, sf::IntRect rec, sf::Vector2f position, sf::Vector2f origin, sf::Vector2f scale, unsigned int alpha = 255, bool repeatTexture = false, bool smooth = true); /// Create SFML sprites advanced inline void createSprite(sf::Texture &tex, sf::Sprite *spr, sf::IntRect rec, sf::Vector2f position, sf::Vector2f origin, sf::Vector2f scale, unsigned int alpha = 255, bool repeatTexture = false, bool smooth = true) { createSprite(tex, *spr, rec, position, origin, scale, alpha, repeatTexture, smooth); } /// Create SFML sprites without IntRec in Vector inline void createSprite(sf::Texture &tex, std::vector &spr, sf::Vector2f position, sf::Vector2f origin, bool smooth = true) { spr.push_back(new sf::Sprite()); createSprite(tex, spr[spr.size() - 1], position, origin, smooth); } /// Create SFML sprites with IntRec in Vector inline void createSprite(sf::Texture &tex, std::vector &spr, sf::IntRect rec, sf::Vector2f position, sf::Vector2f origin, bool repeatTexture = false, bool smooth = true) { spr.push_back(new sf::Sprite()); createSprite(tex, spr[spr.size() - 1], rec, position, origin, repeatTexture, smooth); } /// Create SFML sprites advanced in Vector inline void createSprite(sf::Texture &tex, std::vector &spr, sf::IntRect rec, sf::Vector2f position, sf::Vector2f origin, sf::Vector2f scale, unsigned int alpha = 255, bool repeatTexture = false, bool smooth = true) { spr.push_back(new sf::Sprite()); createSprite(tex, spr[spr.size() - 1], rec, position, origin, scale, alpha, repeatTexture, smooth); } ////////////////////////////////////////////////////// /// \brief Return Cursor Position /// the mouse on PC platform / touch on mobile /// /// \param finger Finger index (on Android) ////////////////////////////////////////////////////// sf::Vector2f getCursor(sf::RenderWindow &window , unsigned int finger = 0); ////////////////////////////////////////////////////// /// \brief Return Cursor Position /// the mouse on PC platform / touch on mobile /// /// \param finger Finger index (on Android) ////////////////////////////////////////////////////// inline sf::Vector2f getCursor(sf::RenderWindow *window , unsigned int finger = 0) { return getCursor(*window, finger); } ////////////////////////////////////////////////////// /// \brief Test the collision of the SFML objects /// with the mouse cursor on PC platform / touch on mobile /// /// \param obj SFML object with which we want to test /// \param finger Finger index (on Android) ////////////////////////////////////////////////////// template bool mouseCollision(sf::RenderWindow &window, T const &obj, unsigned int finger = 0) { sf::Vector2f cursorPos = is::getCursor(window, finger); // A rectangle that will allow to test with the SFML object sf::RectangleShape recCursor(sf::Vector2f(6.f, 6.f)); is::setSFMLObjX_Y(recCursor, cursorPos.x - 3.f, cursorPos.y - 3.f); if (obj.getGlobalBounds().intersects(recCursor.getGlobalBounds())) return true; return false; } ////////////////////////////////////////////////////// /// \brief Test the collision of the SFML objects /// with the mouse cursor on PC platform / touch on mobile /// /// \param obj SFML object with which we want to test /// \param finger Finger index (on Android) ////////////////////////////////////////////////////// template bool mouseCollision(sf::RenderWindow *window, T const *obj, unsigned int finger = 0) { return mouseCollision(*window, *obj, finger); } ////////////////////////////////////////////////////// /// \brief Test the collision of the SFML objects /// with the mouse cursor on PC platform / touch on mobile /// /// \param obj SFML object with which we want to test /// \param position Allows to get the position of the collision point /// \param finger Finger index (on Android) ////////////////////////////////////////////////////// template bool mouseCollision(sf::RenderWindow &window, T const &obj, sf::Vector2f &position, unsigned int finger = 0) { sf::Vector2f cursorPos = is::getCursor(window, finger); setVector2(position, cursorPos.x, cursorPos.y); // A rectangle that will allow to test with the SFML object sf::RectangleShape recCursor(sf::Vector2f(6.f, 6.f)); is::setSFMLObjX_Y(recCursor, position.x - 3.f, position.y - 3.f); if (obj.getGlobalBounds().intersects(recCursor.getGlobalBounds())) return true; return false; } ////////////////////////////////////////////////////// /// \brief Test the collision of the SFML objects /// with the mouse cursor on PC platform / touch on mobile /// /// \param obj SFML object with which we want to test /// \param position Allows to get the position of the collision point /// \param finger Finger index (on Android) ////////////////////////////////////////////////////// template bool mouseCollision(sf::RenderWindow *window, T const *obj, sf::Vector2f &position, unsigned int finger = 0) { return mouseCollision(*window, *obj, position, finger); } /// Do not touch this function it allows to manage the style of the window inline int getWindowStyle() { switch (GameConfig::WINDOW_SETTINGS) { case WindowStyle::NONE : return sf::Style::None; break; case WindowStyle::TITLEBAR : return sf::Style::Titlebar; break; case WindowStyle::RESIZE : return sf::Style::Resize; break; case WindowStyle::CLOSE : return sf::Style::Close; break; case WindowStyle::FULLSCREEN : return sf::Style::Fullscreen; break; default: return sf::Style::Default; break; } } /// Allows to set frame per second template void setFPS(T &render, float fps) { render.setFramerateLimit(fps); } /// Allows to set frame per second template void setFPS(T *render, float fps) { render->setFramerateLimit(fps); } /// Allows to use Android vibrate short vibrate(short duration); //////////////////////////////////////////////////////////// /// \brief Type of action to perform for the openURL function /// //////////////////////////////////////////////////////////// enum OpenURLAction { Http, Email, Tel }; /// Open URL in default navigator /// \param urlStr represent the web url (e.g www.website.com) void openURL(const std::string& url, OpenURLAction action); #if defined(__ANDROID__) /// Convert JNI String to std::string static std::string jstring2string(JNIEnv *env, jstring jStr); /// Return Android terminal device static std::string getDeviceId(JNIEnv *env, ANativeActivity *activity); #endif } #endif // GAME_FONCTION_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/function/GameKeyData.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef GAMEKEYDATA_H_INCLUDED #define GAMEKEYDATA_H_INCLUDED #include "../display/GameDisplay.h" namespace is { ////////////////////////////////////////////////////// /// \brief Class for manage game command /// (Keyboard / Virtual Game Pad for Android) /// ////////////////////////////////////////////////////// class GameKeyData : public MainObject { public: ////////////////////////////////////////////////////// /// \brief Represent game command key /// ////////////////////////////////////////////////////// enum VirtualKeyIndex { V_KEY_LEFT, ///< Represents the Left directional button V_KEY_RIGHT, ///< Represents the Right directional button V_KEY_UP, ///< Represents the Up directional button V_KEY_DOWN, ///< Represents the Down directional button V_KEY_A, ///< Represents the A button V_KEY_B, ///< Represents the B button V_KEY_NONE ///< Represents no button press }; bool m_keyPausePressed, m_keyLeftPressed, m_keyRightPressed, m_keyUpPressed, m_keyDownPressed, m_keyAPressed, m_keyBPressed, m_keyAUsed, m_keyBUsed, m_keyLeftUsed, m_keyRightUsed, m_keyUpUsed, m_keyDownUsed, m_disableAllKey, m_hideGamePad; sf::Keyboard::Key *m_keyboardA = nullptr, *m_keyboardB = nullptr, *m_keyboardLeft = nullptr, *m_keyboardRight = nullptr, *m_keyboardUp = nullptr, *m_keyboardDown = nullptr; VirtualKeyIndex m_moveKeyPressed, ///< Used to find out whether a directional key is pressed m_actionKeyPressed; ///< Used to find out whether a action key (A or B) is pressed GameKeyData(is::GameDisplay *scene); /// Load the image that will serve as Virtual Game Pad (only for Android) /// /// \param usePadColorBlack change the Pad sprite color virtual void loadResources(bool usePadColorBlack = false); /// Manages the positioning of the Virtual Game Pad relative to the screen (only for Android) virtual void step(const float &DELTA_TIME); /// Draw the Virtual Game Pad on the screen (only for Android) virtual void draw(is::Render &surface); /// Check if the Left directional button is pressed virtual bool keyLeftPressed(); /// Check if the Right directional button is pressed virtual bool keyRightPressed(); /// Check if the Up directional button is pressed virtual bool keyUpPressed(); /// Check if the Down directional button is pressed virtual bool keyDownPressed(); /// Check if the A button is pressed virtual bool keyAPressed(); /// Check if the B button is pressed virtual bool keyBPressed(); private: bool virtualKeyPressed(VirtualKeyIndex virtualKeyIndex); bool checkKeyPressed(VirtualKeyIndex virtualKeyIndex, sf::Keyboard::Key *keyboardIndex); float m_moveObj; is::GameDisplay *m_scene; sf::Sprite m_sprJoystick[2]; sf::RectangleShape m_recJoystickMask[2]; sf::RectangleShape m_recKeyLeftMask, m_recKeyRightMask, m_recKeyUpMask, m_recKeyDownMask, m_recKeyAMask, m_recKeyBMask; }; }; #endif // GAMEKEYDATA_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/function/GameKeyName.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef GAMEKEYNAME_H_INCLUDED #define GAMEKEYNAME_H_INCLUDED #include "../islibconnect/isLibConnect.h" namespace is { //////////////////////////////////////////////////////////// /// \brief Returns the name of the keyboard key according to the value /// /// Credit : BlackHC on SFML forum //////////////////////////////////////////////////////////// inline const char *getKeyName(const sf::Keyboard::Key key) { switch(key) { default: case sf::Keyboard::Unknown: return "Unknown"; case sf::Keyboard::A: return "A"; case sf::Keyboard::B: return "B"; case sf::Keyboard::C: return "C"; case sf::Keyboard::D: return "D"; case sf::Keyboard::E: return "E"; case sf::Keyboard::F: return "F"; case sf::Keyboard::G: return "G"; case sf::Keyboard::H: return "H"; case sf::Keyboard::I: return "I"; case sf::Keyboard::J: return "J"; case sf::Keyboard::K: return "K"; case sf::Keyboard::L: return "L"; case sf::Keyboard::M: return "M"; case sf::Keyboard::N: return "N"; case sf::Keyboard::O: return "O"; case sf::Keyboard::P: return "P"; case sf::Keyboard::Q: return "Q"; case sf::Keyboard::R: return "R"; case sf::Keyboard::S: return "S"; case sf::Keyboard::T: return "T"; case sf::Keyboard::U: return "U"; case sf::Keyboard::V: return "V"; case sf::Keyboard::W: return "W"; case sf::Keyboard::X: return "X"; case sf::Keyboard::Y: return "Y"; case sf::Keyboard::Z: return "Z"; case sf::Keyboard::Num0: return "Num0"; case sf::Keyboard::Num1: return "Num1"; case sf::Keyboard::Num2: return "Num2"; case sf::Keyboard::Num3: return "Num3"; case sf::Keyboard::Num4: return "Num4"; case sf::Keyboard::Num5: return "Num5"; case sf::Keyboard::Num6: return "Num6"; case sf::Keyboard::Num7: return "Num7"; case sf::Keyboard::Num8: return "Num8"; case sf::Keyboard::Num9: return "Num9"; case sf::Keyboard::Escape: return "Escape"; case sf::Keyboard::LControl: return "LControl"; case sf::Keyboard::LShift: return "LShift"; case sf::Keyboard::LAlt: return "LAlt"; case sf::Keyboard::RControl: return "RControl"; case sf::Keyboard::RShift: return "RShift"; case sf::Keyboard::RAlt: return "RAlt"; case sf::Keyboard::Menu: return "Menu"; case sf::Keyboard::LBracket: return "LBracket"; case sf::Keyboard::RBracket: return "RBracket"; case sf::Keyboard::SemiColon: return "SemiColon"; case sf::Keyboard::Comma: return "Comma"; case sf::Keyboard::Period: return "Period"; case sf::Keyboard::Slash: return "Slash"; case sf::Keyboard::BackSlash: return "BackSlash"; case sf::Keyboard::Equal: return "Equal"; case sf::Keyboard::Space: return "Space"; case sf::Keyboard::Return: return "Return"; case sf::Keyboard::BackSpace: return "BackSpace"; case sf::Keyboard::Tab: return "Tab"; case sf::Keyboard::PageUp: return "PageUp"; case sf::Keyboard::PageDown: return "PageDown"; case sf::Keyboard::End: return "End"; case sf::Keyboard::Home: return "Home"; case sf::Keyboard::Insert: return "Insert"; case sf::Keyboard::Delete: return "Delete"; case sf::Keyboard::Multiply: return "Multiply"; case sf::Keyboard::Divide: return "Divide"; case sf::Keyboard::Left: return "Left"; case sf::Keyboard::Right: return "Right"; case sf::Keyboard::Up: return "Up"; case sf::Keyboard::Down: return "Down"; case sf::Keyboard::F1: return "F1"; case sf::Keyboard::F2: return "F2"; case sf::Keyboard::F3: return "F3"; case sf::Keyboard::F4: return "F4"; case sf::Keyboard::F5: return "F5"; case sf::Keyboard::F6: return "F6"; case sf::Keyboard::F7: return "F7"; case sf::Keyboard::F8: return "F8"; case sf::Keyboard::F9: return "F9"; case sf::Keyboard::F10: return "F10"; case sf::Keyboard::F11: return "F11"; case sf::Keyboard::F12: return "F12"; case sf::Keyboard::F13: return "F13"; case sf::Keyboard::F14: return "F14"; case sf::Keyboard::F15: return "F15"; case sf::Keyboard::Pause: return "Pause"; #if defined(IS_ENGINE_SFML) case sf::Keyboard::LSystem: return "LSystem"; case sf::Keyboard::RSystem: return "RSystem"; case sf::Keyboard::Quote: return "Quote"; case sf::Keyboard::Tilde: return "Tilde"; case sf::Keyboard::Dash: return "Dash"; case sf::Keyboard::Add: return "Add"; case sf::Keyboard::Subtract: return "Subtract"; case sf::Keyboard::Numpad0: return "Numpad0"; case sf::Keyboard::Numpad1: return "Numpad1"; case sf::Keyboard::Numpad2: return "Numpad2"; case sf::Keyboard::Numpad3: return "Numpad3"; case sf::Keyboard::Numpad4: return "Numpad4"; case sf::Keyboard::Numpad5: return "Numpad5"; case sf::Keyboard::Numpad6: return "Numpad6"; case sf::Keyboard::Numpad7: return "Numpad7"; case sf::Keyboard::Numpad8: return "Numpad8"; case sf::Keyboard::Numpad9: return "Numpad9"; #endif } return ""; } //////////////////////////////////////////////////////////// /// \brief Returns the name of the keyboard key according to the value /// /// Credit : BlackHC on SFML forum //////////////////////////////////////////////////////////// inline std::wstring getKeyWName(const sf::Keyboard::Key key) { switch(key) { default: case sf::Keyboard::Unknown: return L"Unknown"; case sf::Keyboard::A: return L"A"; case sf::Keyboard::B: return L"B"; case sf::Keyboard::C: return L"C"; case sf::Keyboard::D: return L"D"; case sf::Keyboard::E: return L"E"; case sf::Keyboard::F: return L"F"; case sf::Keyboard::G: return L"G"; case sf::Keyboard::H: return L"H"; case sf::Keyboard::I: return L"I"; case sf::Keyboard::J: return L"J"; case sf::Keyboard::K: return L"K"; case sf::Keyboard::L: return L"L"; case sf::Keyboard::M: return L"M"; case sf::Keyboard::N: return L"N"; case sf::Keyboard::O: return L"O"; case sf::Keyboard::P: return L"P"; case sf::Keyboard::Q: return L"Q"; case sf::Keyboard::R: return L"R"; case sf::Keyboard::S: return L"S"; case sf::Keyboard::T: return L"T"; case sf::Keyboard::U: return L"U"; case sf::Keyboard::V: return L"V"; case sf::Keyboard::W: return L"W"; case sf::Keyboard::X: return L"X"; case sf::Keyboard::Y: return L"Y"; case sf::Keyboard::Z: return L"Z"; case sf::Keyboard::Num0: return L"Num0"; case sf::Keyboard::Num1: return L"Num1"; case sf::Keyboard::Num2: return L"Num2"; case sf::Keyboard::Num3: return L"Num3"; case sf::Keyboard::Num4: return L"Num4"; case sf::Keyboard::Num5: return L"Num5"; case sf::Keyboard::Num6: return L"Num6"; case sf::Keyboard::Num7: return L"Num7"; case sf::Keyboard::Num8: return L"Num8"; case sf::Keyboard::Num9: return L"Num9"; case sf::Keyboard::Escape: return L"Escape"; case sf::Keyboard::LControl: return L"LControl"; case sf::Keyboard::LShift: return L"LShift"; case sf::Keyboard::LAlt: return L"LAlt"; case sf::Keyboard::RControl: return L"RControl"; case sf::Keyboard::RShift: return L"RShift"; case sf::Keyboard::RAlt: return L"RAlt"; case sf::Keyboard::Menu: return L"Menu"; case sf::Keyboard::LBracket: return L"LBracket"; case sf::Keyboard::RBracket: return L"RBracket"; case sf::Keyboard::SemiColon: return L"SemiColon"; case sf::Keyboard::Comma: return L"Comma"; case sf::Keyboard::Period: return L"Period"; case sf::Keyboard::Slash: return L"Slash"; case sf::Keyboard::BackSlash: return L"BackSlash"; case sf::Keyboard::Equal: return L"Equal"; case sf::Keyboard::Space: return L"Space"; case sf::Keyboard::Return: return L"Return"; case sf::Keyboard::BackSpace: return L"BackSpace"; case sf::Keyboard::Tab: return L"Tab"; case sf::Keyboard::PageUp: return L"PageUp"; case sf::Keyboard::PageDown: return L"PageDown"; case sf::Keyboard::End: return L"End"; case sf::Keyboard::Home: return L"Home"; case sf::Keyboard::Insert: return L"Insert"; case sf::Keyboard::Delete: return L"Delete"; case sf::Keyboard::Multiply: return L"Multiply"; case sf::Keyboard::Divide: return L"Divide"; case sf::Keyboard::Left: return L"Left"; case sf::Keyboard::Right: return L"Right"; case sf::Keyboard::Up: return L"Up"; case sf::Keyboard::Down: return L"Down"; case sf::Keyboard::F1: return L"F1"; case sf::Keyboard::F2: return L"F2"; case sf::Keyboard::F3: return L"F3"; case sf::Keyboard::F4: return L"F4"; case sf::Keyboard::F5: return L"F5"; case sf::Keyboard::F6: return L"F6"; case sf::Keyboard::F7: return L"F7"; case sf::Keyboard::F8: return L"F8"; case sf::Keyboard::F9: return L"F9"; case sf::Keyboard::F10: return L"F10"; case sf::Keyboard::F11: return L"F11"; case sf::Keyboard::F12: return L"F12"; case sf::Keyboard::F13: return L"F13"; case sf::Keyboard::F14: return L"F14"; case sf::Keyboard::F15: return L"F15"; case sf::Keyboard::Pause: return L"Pause"; #if defined(IS_ENGINE_SFML) case sf::Keyboard::LSystem: return L"LSystem"; case sf::Keyboard::RSystem: return L"RSystem"; case sf::Keyboard::Quote: return L"Quote"; case sf::Keyboard::Tilde: return L"Tilde"; case sf::Keyboard::Dash: return L"Dash"; case sf::Keyboard::Add: return L"Add"; case sf::Keyboard::Subtract: return L"Subtract"; case sf::Keyboard::Numpad0: return L"Numpad0"; case sf::Keyboard::Numpad1: return L"Numpad1"; case sf::Keyboard::Numpad2: return L"Numpad2"; case sf::Keyboard::Numpad3: return L"Numpad3"; case sf::Keyboard::Numpad4: return L"Numpad4"; case sf::Keyboard::Numpad5: return L"Numpad5"; case sf::Keyboard::Numpad6: return L"Numpad6"; case sf::Keyboard::Numpad7: return L"Numpad7"; case sf::Keyboard::Numpad8: return L"Numpad8"; case sf::Keyboard::Numpad9: return L"Numpad9"; #endif } return L""; } } #endif // GAMEKEYNAME_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/function/GameSlider.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef GAMESLIDER_H_INCLUDED #define GAMESLIDER_H_INCLUDED #include "../display/GameDisplay.h" #include "../entity/parents/Type.h" namespace is { class GameSlider : public is::MainObject, public is::Type { public: enum SlideDirection { SLIDE_NONE, SLIDE_UP, SLIDE_DOWN, SLIDE_RIGHT, SLIDE_LEFT }; GameSlider(is::GameDisplay *scene); void step(const float &DELTA_TIME); SlideDirection getSlideDirection() const; private: is::GameDisplay *m_scene; float m_slideDistance; }; } #endif // GAMESLIDER_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/function/GameSystem.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef GAMESYSTEM_H_INCLUDED #define GAMESYSTEM_H_INCLUDED #include "GameFunction.h" #include "../graphic/GRM.h" #include "../sound/GSM.h" ////////////////////////////////////////////////////// // is::Engine version ////////////////////////////////////////////////////// #define IS_ENGINE_VERSION_MAJOR 4 #define IS_ENGINE_VERSION_MINOR 0 #define IS_ENGINE_VERSION_PATCH 2 namespace is { class GameSystem; /// Allows to play sound in container by his name if the option is activated void GSMplaySound(const std::string& name, std::vector>&GSMsound, GameSystem &gameSystem); /// Allows to play music in container by his name if the option is activated void GSMplayMusic(const std::string& name, std::vector> &GSMmusic, GameSystem &gameSystem); ////////////////////////////////////////////////////// /// \brief Class for manage game system /// ////////////////////////////////////////////////////// class GameSystem : public GSM, public GRM { public: ////////////////////////////////////////////////////// /// \brief Represent validation key on PC Platform /// /// It is used to know that button will be used for /// validation during a test ////////////////////////////////////////////////////// enum ValidationButton { ALL_BUTTONS = 0, ///< Represent Mouse and Keyboard validation button (If it is used then it becomes touch action on android) KEYBOARD = -1, ///< Represent Keyboard validation button MOUSE = -2 ///< Represent Mouse validation button (If it is used then it becomes touch action on android) }; GameSystem(sf::RenderWindow &window); ////////////////////////////////////////////////////// /// On PC Platform check if mouse / keyboard validation key button is pressed /// On Android check if user touch screen /// validation key is set in GameConfig.h /// /// \param finger Finger index (on Android) /// \param validationButton Represents the validation button to use to take the test ////////////////////////////////////////////////////// virtual bool isPressed(int finger = 0) const; virtual bool isPressed(ValidationButton validationButton) const; ////////////////////////////////////////////////////// /// \brief Check if key is pressed /// /// \return true if key is pressed false if not ////////////////////////////////////////////////////// virtual bool keyIsPressed(sf::Keyboard::Key key) const; /* * When using is::Engine to develop on HTML 5 the keyboard and mouse keys are represented * by integers. They are no longer differentiated by an enum, so this function is no longer * useful when using the SDK which allows to develop on the web. */ ////////////////////////////////////////////////////// /// \brief Check if mouse button is pressed /// /// \return true if button is pressed false if not ////////////////////////////////////////////////////// virtual bool keyIsPressed(sf::Mouse::Button button) const; ////////////////////////////////////////////////////// /// \brief Check if file exist /// /// \return true is file is found false if not ////////////////////////////////////////////////////// static bool fileExist(const std::string &fileName); /// Allows to remove file static void removeFile(const std::string &fileName); /// Allows to play a sound if the option is activated virtual void playSound(sf::Sound &obj); /// Allows to play a sound if the option is activated virtual void playSound(sf::Sound *obj); /// Allows to play sound in container by his name if the option is activated virtual void GSMplaySound(const std::string& name) { is::GSMplaySound(name, m_GSMsound, *this); } /// Allows to play a music if the option is activated virtual void playMusic(sf::Music &obj); /// Allows to play a music if the option is activated virtual void playMusic(sf::Music *obj); /// Allows to play music in container by his name if the option is activated virtual void GSMplayMusic(const std::string& name) { is::GSMplayMusic(name, //#if !defined(__ANDROID__) m_GSMmusic //#else // m_GSMsound //#endif , *this); } /// Allows to stop a sound virtual void stopSound(sf::Sound &obj); /// Allows to stop a music virtual void stopMusic(sf::Music &obj); //////////////////////////////////////////////////////////// /// These methods below have the same role as those above. /// The difference here is that their name starting with GSM /// is replaced by GRM (Game Resource Manager). //////////////////////////////////////////////////////////// /// Allows to play sound in container by his name if the option is activated virtual void GRMplaySound(const std::string& name) { GSMplaySound(name); } /// Allows to play music in container by his name if the option is activated virtual void GRMplayMusic(const std::string& name) { GSMplayMusic(name); } //////////////////////////////////////////////////////////// /// //////////////////////////////////////////////////////////// /// Allows to use vibrate if the option is activated (only for Android) /// \param ms representing the duration of the vibrator in millisecond virtual void useVibrate(short ms); /// Save game configuration data virtual void saveConfig(const std::string &fileName); /// Load game configuration data virtual void loadConfig(const std::string &fileName); /// Save virtual game pad configuration data virtual void savePadConfig(const std::string &fileName); /// Load virtual game pad configuration data virtual void loadPadConfig(const std::string &fileName); //////////////////////////////////////////////////////////// // Do not touch these variables unless you know what you are doing bool m_disableKey; ///< If it is @a true all the engine functions that manage the inputs are deactivated (keyboard, mouse, touch) bool m_enableSound; ///< Used to find out if sound is enabled in option bool m_enableMusic; ///< Used to find out if music is enabled in option bool m_enableVibrate; ///< Used to find out if vibrate is enabled in option bool m_keyIsPressed; ///< Used to find out if a key / button has been pressed bool m_firstLaunch; ///< Lets check if the game has been launched once bool m_loadParentResources; ///< Allows to load parents resources once /// Represent the variable that stores the option validation key with the Mouse sf::Mouse::Button *m_validationMouseKey = nullptr; /// Represent the variable that stores the option validation key with the Keyboard sf::Keyboard::Key *m_validationKeyboardKey = nullptr; int m_gameLanguage; ///< Represents the index of the chosen language int m_padAlpha; ///< Use to change the transparency of the Virtual Game Pad // These variable allows to position the Virtual Game Pad float m_padDirXPos, m_padDirYPos, m_padActionXPos, m_padActionYPos; float m_defaultPadDirXPos, m_defaultPadDirYPos, m_defaultPadActionXPos, m_defaultPadActionYPos; bool m_permutePadAB; /// Application sf::RenderWindow &m_window; //////////////////////////////////////////////////////////// }; } #endif // GAMESYSTEM_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/function/GameTime.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef GAMETIME_H_INCLUDED #define GAMETIME_H_INCLUDED #include namespace is { //////////////////////////////////////////////////////////// /// \brief Class to manage the time of the game like a stopwatch /// /// To use it you define a time (minute, second, third) with the /// constructor or a method then apply its step() method which /// allows you to start the countdown until the minutes, seconds /// and thirds reach zero (0) //////////////////////////////////////////////////////////// class GameTime { public: GameTime(); //////////////////////////////////////////////////////////// /// Constructor to initialize the time with the milliseconds /// which are distributed in minutes and seconds //////////////////////////////////////////////////////////// GameTime(unsigned int ms); //////////////////////////////////////////////////////////// /// Constructor to initialize the time with the minutes /// seconds and milliseconds //////////////////////////////////////////////////////////// GameTime(unsigned int m, unsigned int s, unsigned int ms = 0); ~GameTime(); /// Start the countdown of time so that it stops at zero (0) void step(const float &DELTA_TIME); /// Add the minute, second and millisecond to the current time void addTimeValue(int m, int s, int ms); /// Set a new minute, second and millisecond to the current time void setTimeValue(int m, int s, int ms); /// Set the milliseconds which are distributed in minutes and seconds void setMSecond(int ms); /// Returns current time in the form of a string (e.g 00:00.00) const std::string getTimeString() const noexcept; /// Returns time in milliseconds unsigned int getTimeValue() const; /// Returns the minute unsigned int getMinute() const; /// Returns the second unsigned int getSecond() const; /// Returns the millisecond unsigned int getMSecond() const; /// Compare the entered time and the time of the object /// \return true if the time entered is greater than the time of the object false if not bool compareTime(unsigned int m, unsigned int s = 0, unsigned int ms = 0) const; /// Equality comparison operator GameTime& operator=(GameTime const &t); /// Operator to display the time with std::cout friend std::ostream& operator<<(std::ostream &flux, GameTime const &t); protected: unsigned int m_minute, m_second, m_mSecond; }; bool operator==(GameTime const &t1, GameTime const &t2); bool operator>(GameTime const &t1, GameTime const &t2); bool operator<(GameTime const &t1, GameTime const &t2); } #endif // GAMETIME_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/graphic/GRM.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef GRM_H_INCLUDED #define GRM_H_INCLUDED #include "GameFont.h" #include "GameTexture.h" namespace is { //////////////////////////////////////////////////////////// /// Class for manage SFML Font and Texture without using an /// SFML object //////////////////////////////////////////////////////////// class GRM { public: /// Game Font container std::vector> m_GRMfont; /// Game Texture container std::vector> m_GRMtexture; ////////////////////////////////////////////////////// /// \brief Allows to add SFML Font in GRM container /// /// \param name of font which will be used to identify /// it in the container in order to be able to access it /// \param filePath path of the font file to add ////////////////////////////////////////////////////// virtual sf::Font& GRMaddFont(const std::string& name, const std::string& filePath) { auto obj = fontFileExists(filePath); if (obj == nullptr) { auto newObj = std::make_shared(name, filePath); m_GRMfont.push_back(newObj); return newObj->getFont(); } else is::showLog("WARNING: <" + name + "> font has already been added!"); return obj->getFont(); } ////////////////////////////////////////////////////// /// \brief Allows to add existing font in GRM container /// /// \param font object ////////////////////////////////////////////////////// void GRMaddFontObject(std::shared_ptr font, bool showError = true) { if (!fontFileExists(font->getFilePath())) m_GRMfont.push_back(font); else { if (showError) is::showLog("WARNING: <" + font->getName() + "> font has already been added!"); } } ////////////////////////////////////////////////////// /// \brief Allows to add SFML Texture in GRM container /// /// \param name of texture which will be used to identify /// it in the container in order to be able to access it /// \param filePath path of the texture file to add ////////////////////////////////////////////////////// virtual sf::Texture& GRMaddTexture(const std::string& name, const std::string& filePath) { auto obj = textureFileExists(filePath); if (obj == nullptr) { auto newObj = std::make_shared(name, filePath); m_GRMtexture.push_back(newObj); return newObj->getTexture(); } else is::showLog("WARNING: <" + name + "> texture has already been added!"); return obj->getTexture(); } ////////////////////////////////////////////////////// /// \brief Allows to add existing texture in GRM container /// /// \param texture object ////////////////////////////////////////////////////// void GRMaddTextureObject(std::shared_ptr texture, bool showError = true) { if (textureFileExists(texture->getFilePath()) == nullptr) m_GRMtexture.push_back(texture); else { if (showError) is::showLog("WARNING: <" + texture->getName() + "> texture has already been added!"); } } /// Allows to get font as a reference in container by his name virtual sf::Font& GRMgetFont(const std::string& name) {return *GRMgetFontPtr(name);} /// Allows to get texture as a reference in container by his name virtual sf::Texture& GRMgetTexture(const std::string& name) {return *GRMgetTexturePtr(name);} /// Allows to get font as a pointer in container by his name virtual sf::Font* GRMgetFontPtr(const std::string& name) { WITH(m_GRMfont.size()) { if (m_GRMfont[_I].get() != nullptr) { if (m_GRMfont[_I]->getName() == name && m_GRMfont[_I]->getFileIsLoaded()) { return &m_GRMfont[_I]->getFont(); } } } is::showLog("ERROR: <" + name + "> font does not exist!"); return nullptr; } /// Allows to get texture as a pointer in container by his name virtual sf::Texture* GRMgetTexturePtr(const std::string& name) { WITH(m_GRMtexture.size()) { if (m_GRMtexture[_I].get() != nullptr) { if (m_GRMtexture[_I]->getName() == name && m_GRMtexture[_I]->getFileIsLoaded()) { return &m_GRMtexture[_I]->getTexture(); } } } is::showLog("ERROR: <" + name + "> texture does not exist!"); return nullptr; } /// Allows to delete font in container by his name void GRMdeleteFont(const std::string& name) { int fontId(-1); WITH(m_GRMfont.size()) { if (m_GRMfont[_I].get() != nullptr) { if (m_GRMfont[_I]->getName() == name) { fontId = _I; break; } } } if (fontId == -1) is::showLog("ERROR: Can't delete <" + name + "> font because font does not exist!"); else { m_GRMfont[fontId].reset(); m_GRMfont[fontId] = nullptr; } } /// Allows to delete texture in container by his name void GRMdeleteTexture(const std::string& name) { int textureId(-1); WITH(m_GRMtexture.size()) { if (m_GRMtexture[_I].get() != nullptr) { if (m_GRMtexture[_I]->getName() == name) { textureId = _I; break; } } } if (textureId == -1) is::showLog("ERROR: Can't delete <" + name + "> texture because texture does not exist!"); else { m_GRMtexture[textureId].reset(); m_GRMtexture[textureId] = nullptr; } } private: GameTexture* textureFileExists(const std::string& filePath) const { WITH(m_GRMtexture.size()) { if (m_GRMtexture[_I].get() != nullptr) { if (m_GRMtexture[_I]->getFilePath() == filePath) return m_GRMtexture[_I].get(); } } return nullptr; } GameFont* fontFileExists(const std::string& filePath) const { WITH(m_GRMfont.size()) { if (m_GRMfont[_I].get() != nullptr) { if (m_GRMfont[_I]->getFilePath() == filePath) return m_GRMfont[_I].get(); } } return nullptr; } }; } #endif // GRM_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/graphic/GameFont.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef GAMEFONT_H_INCLUDED #define GAMEFONT_H_INCLUDED #include "../islibconnect/isLibConnect.h" #include "../entity/parents/Name.h" #include "../entity/parents/FilePath.h" namespace is { //////////////////////////////////////////////////////////// /// Class for manage SFML Font //////////////////////////////////////////////////////////// class GameFont : public is::Name, public is::FilePath { public: GameFont(const std::string& fontName, const std::string& filePath): Name(fontName), FilePath(filePath) { if (m_font.loadFromFile(m_strFilePath)) m_fileIsLoaded = true; //else showLog("ERROR: Can't load font : " + filePath); } virtual ~GameFont() {} void loadResources(const std::string& filePath) { if (m_font.loadFromFile(filePath)) { m_strFilePath = filePath; m_fileIsLoaded = true; } else { m_fileIsLoaded = false; //showLog("ERROR: Can't load font : " + filePath); } } /// Return font sf::Font& getFont() {return m_font;} private: sf::Font m_font; }; } #endif // GAMEFONT_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/graphic/GameTexture.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef GAMETEXTURE_H_INCLUDED #define GAMETEXTURE_H_INCLUDED #include "../islibconnect/isLibConnect.h" #include "../entity/parents/Name.h" #include "../entity/parents/FilePath.h" namespace is { //////////////////////////////////////////////////////////// /// Class for manage SFML Texture //////////////////////////////////////////////////////////// class GameTexture : public is::Name, public is::FilePath { public: GameTexture(const std::string& textureName, const std::string& filePath): Name(textureName), FilePath(filePath) { if (m_tex.loadFromFile(m_strFilePath)) m_fileIsLoaded = true; //else showLog("ERROR: Can't load texture : " + filePath); } virtual ~GameTexture() {} void loadResources(const std::string& filePath) { if (m_tex.loadFromFile(filePath)) { m_strFilePath = filePath; m_fileIsLoaded = true; } else { m_fileIsLoaded = false; //showLog("ERROR: Can't load texture : " + filePath); } } /// Return texture sf::Texture& getTexture() {return m_tex;} private: sf::Texture m_tex; }; } #endif // GAMETEXTURE_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/graphic/TransitionEffect.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef TRANSITIONEFFECT_H_INCLUDED #define TRANSITIONEFFECT_H_INCLUDED #include "../display/GameDisplay.h" #include "../entity/parents/Type.h" namespace is { class TransitionEffect : public is::MainObject, public is::Type { public: enum Transition { FADE_IN, FADE_OUT }; TransitionEffect(is::GameDisplay *scene); void step(const float &DELTA_TIME); void draw(is::Render &render); void setType(int type) { if ((m_type == FADE_OUT && type == FADE_IN) || (m_type == FADE_IN && type == FADE_OUT)) m_transitionEnd = false; m_type = type; } bool getTransitionEnd(int type) const {return (m_transitionEnd && m_type == type);} sf::RectangleShape& getRecTransition() {return m_recTransition;} private: is::GameDisplay *m_scene; sf::RectangleShape m_recTransition; bool m_transitionEnd; }; } #endif // TRANSITIONEFFECT_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/islibconnect/isEngineSDLWrapper.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef ISENGINESDLWRAPPER_H_INCLUDED #define ISENGINESDLWRAPPER_H_INCLUDED #include "isEngineWrapper.h" #if defined(IS_ENGINE_SDL_2) #if defined(__ANDROID__) #include #include #endif #if defined(IS_ENGINE_HTML_5) #include #endif #include #include #include #include #include #include #include #include #include #if defined(IS_ENGINE_SWITCH) #include #endif #define NUM_BUTTONS SDL_CONTROLLER_BUTTON_MAX namespace is { extern SDL_Window *IS_ENGINE_SDL_window; extern SDL_Renderer *IS_ENGINE_SDL_renderer; extern SDL_DisplayMode IS_ENGINE_SDL_displayMode; // Used to determine the size of a window /// Allows to use touch functions extern bool IS_ENGINE_MOBILE_OS; /// Allow to avoid bug static bool IS_ENGINE_SDL_closeWindow = false; /// These variables are used to store the scale of the screen in order to /// resize the images on Android (Sprite, Text, ...) extern float IS_ENGINE_SDL_screenXScale, IS_ENGINE_SDL_screenYScale; /// Max SDL Sound Channel static const short IS_ENGINE_SDL_CHANNEL_MAX = 60; /// Allow to avoid duplicates extern short IS_ENGINE_SDL_channel[IS_ENGINE_SDL_CHANNEL_MAX]; /// Class that allows to manage touchdowns class TouchData { public: bool m_SDLtouchDown; int m_SDLtouchX; int m_SDLtouchY; }; /// Maximum number of fingers to have on the screen static const short IS_ENGINE_SDL_TOUCH_ID_COUNT_MAX = 2; /// Stores the different fingers used on the screen extern TouchData IS_ENGINE_SDL_touchData[IS_ENGINE_SDL_TOUCH_ID_COUNT_MAX]; /// Table simulating the state of the "keys" extern Uint8 IS_ENGINE_SDL_buttonState[NUM_BUTTONS]; /// Pointer to the active controller extern SDL_GameController *IS_ENGINE_SDL_SDL_GameController; /// Function to call every frame to update the state of the buttons void updateButtonState(); /// Function simulating SDL_GetKeyboardState but for gamepad buttons const Uint8* getControllerButtonState(int *numButtons); /// Allows to initialize the resources of the SDL library bool SDL2initLib(); /// Allows to free the resources of the SDL library at the end of the program void SDL2freeLib(); } namespace sf { class Color { public: int r, g, b, a; Color(): r(255), g(255), b(255), a(255) {} Color(int red, int green, int blue, int alpha): r(red), g(green), b(blue), a(alpha) {} Color(int red, int green, int blue): r(red), g(green), b(blue), a(255) {} static Color White; ///< White static Color Black; ///< Black static Color Grey; ///< Grey static Color Red; ///< Red static Color Green; ///< Green static Color Blue; ///< Blue static Color Yellow; ///< Yellow static Color Magenta; ///< Magenta static Color Cyan; ///< Cyan static Color Transparent; ///< Transparent void operator =(const Color &color) { r = color.r; g = color.g; b = color.b; a = color.a; } }; class Texture { public: Texture() {} Texture(const std::string& filename, bool useWithVertices = false) {loadSurface(filename, useWithVertices);} Texture(SDL_Texture* tex, int w, int h) : m_texture(tex) { m_size.x = w; m_size.y = h; } ~Texture(); const Vector2u& getSize() const noexcept { return m_size; } bool loadFromFile(const std::string& filename, bool useWithVertices = false) { return loadSurface(filename, useWithVertices); } SDL_Surface* getSDLSurface() const {return m_SDLsurface;} const std::string &getFileName() const noexcept { return m_filename; } SDL_Texture* getSDLTexture() const {return m_texture;} /* void loadFromImage() {functionNotSupported("Texture", "loadFromImage", "loadSFMLTexture");} void loadFromMemory() {functionNotSupported("Texture", "loadFromMemory", "loadSFMLTexture");} void loadFromStream() {functionNotSupported("Texture", "loadFromStream", "loadSFMLTexture");} */ SDL_Texture* m_texture; private: SDL_Surface *m_SDLsurface = NULL; Vector2u m_size; std::string m_filename = ""; bool loadSurface(const std::string& filePath, bool useWithVertices = false); }; class Font { public: Font() {} Font(const std::string& filename, int size): m_size(size) {loadFont(filename);} ~Font(); int m_SDLoutlineFontSize = 0; Uint32 m_SDLFontStyle = TTF_STYLE_NORMAL; void setSDLFontSize(int size) {m_size = size;} const std::string& getFileName() const noexcept {return m_filename;} int getSize() const {return m_size;} bool loadFromFile(const std::string& filename) {return loadFont(filename);} TTF_Font* getSDLFont() const {return m_SDLfont;} /* void loadFromMemory() {functionNotSupported("Font", "loadFromMemory", "loadSFMLFont");} void loadFromStream() {functionNotSupported("Font", "loadFromStream", "loadSFMLFont");} */ private: TTF_Font *m_SDLfont = NULL; int m_size = 20; std::string m_filename = ""; bool loadFont(const std::string& filename); }; } namespace is { /// auto generate font container /// Allows to store fonts that will be used to manipulate the /// size of texts extern std::vector IS_ENGINE_SDL_AUTO_GENERATE_FONT; } namespace sf { class Transformable { public: Transformable(); Transformable(Texture &texture); void setPosition(float x, float y) { is::setVector2(m_position, x, y); } void setPosition(const Vector2f &v) { setPosition(v.x, v.y); } void move(const Vector2f &v) { move(v.x, v.y); } void move(float x, float y) { setPosition(m_position.x + x, m_position.y + y); } void setScale(float x, float y) { is::setVector2(m_scale, x, y); } void setScale(const Vector2f &v) { setScale(v.x, v.y); } void scale(float x, float y) { is::setVector2(m_scale, m_scale.x + x, m_scale.y + y); } void scale(const Vector2f &v) { setScale(v.x, v.y); } virtual void setSize(float x, float y) { is::setVector2(m_size, x, y); m_textureRec.width = x; m_textureRec.height = y; } virtual void setSize(const Vector2f &size) { setSize(size.x, size.y); } void setOrigin(float x, float y) { is::setVector2(m_origin, x, y); } void setOrigin(const Vector2f &v) { setOrigin(v.x, v.y); } void setRotation(float angle); void rotate(float angle) { setRotation(m_rotation + angle); } virtual void setColor(int r, int g, int b, int a); virtual void setColor(Color const &color) { setColor(color.r, color.g, color.b, color.a); } virtual void setFillColor(Color const &color) { setColor(color.r, color.g, color.b, color.a); } virtual const Vector2f& getPosition() const noexcept {return m_position;} virtual const Vector2f& getScale() const noexcept {return m_scale;} virtual const Vector2f& getSize() const noexcept {return m_size;} virtual const Vector2f& getOrigin() const noexcept {return m_origin;} virtual float getRotation() const {return m_rotation;} virtual Rect getGlobalBounds() const {return functionGetGlobalBounds(m_position, m_origin, m_size);} virtual const Color& getColor() const noexcept {return m_color;} virtual const Color& getFillColor() const noexcept {return getColor();} Texture* getTexture() const {return m_texture;} virtual const Rect getTextureRect() const noexcept {return m_textureRec;} virtual const SDL_Color& getSDLColor(bool getAlpha); protected: SDL_Color m_SDLcolor; Rect m_textureRec; Texture *m_texture = nullptr; float m_rotation = 0.f; Vector2f m_position; Vector2f m_scale; Vector2f m_size; Vector2f m_origin; Color m_color; }; /// Class that allows to create SDL textures in order to use it to /// create SFML Sprites, Image and Texts. /// On SDL texts are created using textures and surfaces class SDLTexture : public Transformable { public: SDL_RendererFlip m_SDLFlip = SDL_FLIP_NONE; bool m_multiLines = false; bool m_circleShape = true; Rect m_SDLoutlineTextureRec; enum SDLTextureType { IS_ENGINE_SDL_SPRITE, IS_ENGINE_SDL_TEXT }; SDLTextureType m_SDLTextureType = IS_ENGINE_SDL_SPRITE; SDLTexture() : Transformable() {} SDLTexture(Texture &texture) : Transformable(texture) {} ~SDLTexture(); void setTextureRect(IntRect rec); SDL_Texture* getSDLTexture() const {return m_SDLtexture;} SDL_Texture* getSDLOutlineTexture() const {return m_SDLoutlineTexture;} protected: SDL_Texture *m_SDLtexture = NULL; SDL_Texture *m_SDLoutlineTexture = NULL; }; class Sprite : public SDLTexture { public: Sprite() : SDLTexture() {} virtual ~Sprite() {} Sprite(Texture &texture) : SDLTexture(texture) {setSDLTexture();} //-->Sprite(RenderTexture &renderTexture) : Transformable(renderTexture) {setSDLTexture();} void setTexture(sf::Texture& texture); protected: void setSDLTexture(); }; class Image : public SDLTexture { public: Image() : SDLTexture() {} ~Image(); bool loadFromFile(const std::string& filename); const Uint8* getPixelsPtr() const; }; class Text : public SDLTexture { public: enum Style { Regular = TTF_STYLE_NORMAL, ///< Regular characters, no style Bold = TTF_STYLE_BOLD, ///< Bold characters Italic = TTF_STYLE_ITALIC, ///< Italic characters Underlined = TTF_STYLE_UNDERLINE, ///< Underlined characters StrikeThrough = TTF_STYLE_STRIKETHROUGH ///< Strike through characters }; bool m_SDLcontainMultiSpaces = false; short m_SDLaddTextRecWSize = 3; Text(): SDLTexture() {m_SDLTextureType = IS_ENGINE_SDL_TEXT;} Text(sf::Font& font); Text(sf::Font& font, const std::string& text); Text(sf::Font& font, const std::wstring& text); ~Text(); void setFont(sf::Font &font); void setString(const std::wstring& text); void setString(const wchar_t& text); void setString(const std::string& text); void setString(const char& text); void setColor(int r, int g, int b, int a); void setOrigin(float x, float y); void setColor(Color const &color) { setColor(color.r, color.g, color.b, color.a); } void setFillColor(Color const &color) { setColor(color.r, color.g, color.b, color.a); } void setCharacterSize(int size); void setStyle(Uint32 style); void setOutlineColor(const Color& color); void setOutlineThickness(float thickness); Font *getFont() const {return m_font;} const std::string &getString() const noexcept { return m_string; } const std::wstring &getWString() const noexcept { return m_wstring; } int getCharacterSize() {return m_characterSize;} Uint32 getStyle() const {return m_style;} const Color& getOutlineColor() const {return m_outlineColor;} float getOutlineThickness() const {return m_outlineThickness;} private: SDL_Surface *m_SDLsurface = NULL; Font *m_font = nullptr; SDL_Surface *m_SDLoutlineSurface = NULL; Font *m_outlineFont = nullptr; SDL_Color m_SDLoutlineColor; const SDL_Color& getSDLOutlineColor() { m_SDLoutlineColor.r = m_outlineColor.r; m_SDLoutlineColor.g = m_outlineColor.g; m_SDLoutlineColor.b = m_outlineColor.b; m_SDLoutlineColor.a = m_outlineColor.a; return m_SDLoutlineColor; } std::string m_string = ""; std::string m_tempString = ""; std::wstring m_wstring = L""; std::wstring m_tempWstring = L""; int m_characterSize = 0; int m_currentCharSize = 0; char *m_SDLtext = nullptr; Uint32 m_style; Color m_outlineColor; int m_outlineThickness = 0; void setObjectText(const std::string& text); void setObjectText(const std::wstring& text); /// Used to create a text with a Texture, Surface and a font. bool setSDLText(); }; class View { public: View(); View(const Vector2f& center, const Vector2f& size); void setCenter(float x, float y); void setCenter(const Vector2f& center) { setCenter(center.x, center.y); } void setSize(float width, float height); void setSize(const Vector2f& size) { setSize(size.x, size.y); } const Vector2f& getSize() const noexcept {return m_size;} const Vector2f& getCenter() const noexcept; private: sf::Vector2f m_size; sf::Vector2f m_center; }; class Shape : public Transformable { public: Shape() : Transformable() {} void setOutlineColor(const Color& color) {m_outlineColor = color;} void setOutlineThickness(float thickness) {m_outlineThickness = thickness;} virtual void draw(View const &view) = 0; const Color& getOutlineColor() const {return m_outlineColor;} float getOutlineThickness() const {return m_outlineThickness;} protected: Color m_outlineColor; float m_outlineThickness = 0.f; }; class RectangleShape : public Shape { public: RectangleShape() : Shape() {} virtual ~RectangleShape() {} RectangleShape(float width, float height) : Shape() {setSize(width, height);} RectangleShape(const Vector2f &size) : Shape() {setSize(size.x, size.y);} void draw(View const &view); }; class CircleShape : public Shape { public: CircleShape(): Shape() {} CircleShape(float raduis) : Shape() {setRadius(raduis);} void setRadius(float raduis) {setSize(raduis, raduis);} float getRadius() {return m_size.x;} void draw(View const &view); }; //--- New SFML Classes Simulation // sf::PrimitiveType enum class PrimitiveType { Points, Lines, LineStrip, Triangles, TriangleStrip, TriangleFan, Quads }; // sf::Vertex struct Vertex { Vector2f position; Color color; Vector2f texCoords; Vertex(const Vector2f& pos = Vector2f(), const Color& col = Color(), const Vector2f& tex = Vector2f()) : position(pos), color(col), texCoords(tex) {} }; // sf::Transform class Transform { public: Transform() { matrix[0] = 1.0f; matrix[1] = 0.0f; matrix[2] = 0.0f; matrix[3] = 0.0f; matrix[4] = 1.0f; matrix[5] = 0.0f; matrix[6] = 0.0f; matrix[7] = 0.0f; matrix[8] = 1.0f; } Transform translate(float x, float y) const { Transform t; t.matrix[2] = x; t.matrix[5] = y; return combine(t); } Transform rotate(float angle, float centerX = 0.0f, float centerY = 0.0f) const { float rad = angle * 3.1415926535f / 180.0f; float cosA = std::cos(rad); float sinA = std::sin(rad); Transform t; t.matrix[0] = cosA; t.matrix[1] = sinA; t.matrix[3] = -sinA; t.matrix[4] = cosA; t.matrix[2] = centerX * (1 - cosA) + centerY * sinA; t.matrix[5] = centerY * (1 - cosA) - centerX * sinA; return combine(t); } Transform scale(float scaleX, float scaleY, float centerX = 0.0f, float centerY = 0.0f) const { Transform t; t.matrix[0] = scaleX; t.matrix[4] = scaleY; t.matrix[2] = centerX * (1 - scaleX); t.matrix[5] = centerY * (1 - scaleY); return combine(t); } Vector2f transformPoint(const Vector2f& point) const { return Vector2f( point.x * matrix[0] + point.y * matrix[3] + matrix[2], point.x * matrix[1] + point.y * matrix[4] + matrix[5] ); } Transform combine(const Transform& other) const { Transform result; result.matrix[0] = matrix[0] * other.matrix[0] + matrix[3] * other.matrix[1]; result.matrix[1] = matrix[1] * other.matrix[0] + matrix[4] * other.matrix[1]; result.matrix[2] = matrix[2] * other.matrix[0] + matrix[5] * other.matrix[1] + other.matrix[2]; result.matrix[3] = matrix[0] * other.matrix[3] + matrix[3] * other.matrix[4]; result.matrix[4] = matrix[1] * other.matrix[3] + matrix[4] * other.matrix[4]; result.matrix[5] = matrix[2] * other.matrix[3] + matrix[5] * other.matrix[4] + other.matrix[5]; result.matrix[6] = matrix[0] * other.matrix[6] + matrix[3] * other.matrix[7] + matrix[6]; result.matrix[7] = matrix[1] * other.matrix[6] + matrix[4] * other.matrix[7] + matrix[7]; result.matrix[8] = matrix[2] * other.matrix[6] + matrix[5] * other.matrix[7] + matrix[8]; return result; } private: float matrix[9]; // 3x3 matrix: [a b tx; c d ty; 0 0 1] }; // sf::RenderStates struct RenderStates { const Texture* texture; Transform transform; RenderStates(const Texture* tex = nullptr) : texture(tex) {} RenderStates(const Transform& t) : texture(nullptr), transform(t) {} RenderStates(const Texture* tex, const Transform& t) : texture(tex), transform(t) {} }; // sf::VertexArray class VertexArray { public: VertexArray(PrimitiveType type = PrimitiveType::Points, size_t vertexCount = 0) : primitiveType(type), vertices(vertexCount) {} void append(const Vertex& vertex) { vertices.push_back(vertex); } void insert(size_t index, const Vertex& vertex) { if (index > vertices.size()) { throw std::out_of_range("Vertex index out of range"); } vertices.insert(vertices.begin() + index, vertex); } void remove(size_t index) { if (index >= vertices.size()) { throw std::out_of_range("Vertex index out of range"); } vertices.erase(vertices.begin() + index); } void clear() { vertices.clear(); } size_t getVertexCount() const { return vertices.size(); } void reserve(size_t count) { vertices.reserve(count); } Vertex& operator[](size_t index) { if (index >= vertices.size()) { throw std::out_of_range("Vertex index out of range"); } return vertices[index]; } const Vertex& operator[](size_t index) const { if (index >= vertices.size()) { throw std::out_of_range("Vertex index out of range"); } return vertices[index]; } void resize(size_t vertexCount) { vertices.resize(vertexCount); } PrimitiveType getPrimitiveType() const { return primitiveType; } void setPrimitiveType(PrimitiveType type) { primitiveType = type; } FloatRect getBounds() const { if (vertices.empty()) { return FloatRect(); } float left = vertices[0].position.x; float top = vertices[0].position.y; float right = left; float bottom = top; for (const auto& vertex : vertices) { left = std::min(left, vertex.position.x); right = std::max(right, vertex.position.x); top = std::min(top, vertex.position.y); bottom = std::max(bottom, vertex.position.y); } return FloatRect(left, top, right - left, bottom - top); } FloatRect getTransformedBounds() const { if (vertices.empty()) { return FloatRect(); } Vector2f transformed[4]; for (size_t i = 0; i < vertices.size(); ++i) { transformed[i] = transform.transformPoint(vertices[i].position); } float left = transformed[0].x; float top = transformed[0].y; float right = left; float bottom = top; for (const auto& pos : transformed) { left = std::min(left, pos.x); right = std::max(right, pos.x); top = std::min(top, pos.y); bottom = std::max(bottom, pos.y); } return FloatRect(left, top, right - left, bottom - top); } void setTransform(const Transform& t) { transform = t; } const Transform& getTransform() const { return transform; } void draw(SDL_Renderer* renderer, const RenderStates& states = RenderStates()) const { Transform combinedTransform = states.transform.combine(transform); switch (primitiveType) { case PrimitiveType::Points: for (const auto& vertex : vertices) { Vector2f pos = combinedTransform.transformPoint(vertex.position); SDL_SetRenderDrawColor(renderer, vertex.color.r, vertex.color.g, vertex.color.b, vertex.color.a); SDL_RenderDrawPoint(renderer, static_cast(pos.x), static_cast(pos.y)); } break; case PrimitiveType::Lines: for (size_t i = 0; i + 1 < vertices.size(); i += 2) { Vector2f pos1 = combinedTransform.transformPoint(vertices[i].position); Vector2f pos2 = combinedTransform.transformPoint(vertices[i + 1].position); SDL_SetRenderDrawColor(renderer, vertices[i].color.r, vertices[i].color.g, vertices[i].color.b, vertices[i].color.a); SDL_RenderDrawLine(renderer, static_cast(pos1.x), static_cast(pos1.y), static_cast(pos2.x), static_cast(pos2.y)); } break; case PrimitiveType::LineStrip: for (size_t i = 0; i + 1 < vertices.size(); ++i) { Vector2f pos1 = combinedTransform.transformPoint(vertices[i].position); Vector2f pos2 = combinedTransform.transformPoint(vertices[i + 1].position); SDL_SetRenderDrawColor(renderer, vertices[i].color.r, vertices[i].color.g, vertices[i].color.b, vertices[i].color.a); SDL_RenderDrawLine(renderer, static_cast(pos1.x), static_cast(pos1.y), static_cast(pos2.x), static_cast(pos2.y)); } break; case PrimitiveType::Triangles: if (states.texture) { for (size_t i = 0; i + 2 < vertices.size(); i += 3) { Vector2f pos[3]; for (int j = 0; j < 3; ++j) { pos[j] = combinedTransform.transformPoint(vertices[i + j].position); } float minX = std::min(std::min(pos[0].x, pos[1].x), pos[2].x); float minY = std::min(std::min(pos[0].y, pos[1].y), pos[2].y); float maxX = std::max(std::max(pos[0].x, pos[1].x), pos[2].x); float maxY = std::max(std::max(pos[0].y, pos[1].y), pos[2].y); SDL_Rect dest = { static_cast(minX), static_cast(minY), static_cast(maxX - minX), static_cast(maxY - minY) }; SDL_Rect src = { static_cast(vertices[i].texCoords.x), static_cast(vertices[i].texCoords.y), static_cast(states.texture->getSize().x), static_cast(states.texture->getSize().y) }; SDL_SetTextureColorMod(states.texture->getSDLTexture(), vertices[i].color.r, vertices[i].color.g, vertices[i].color.b); SDL_SetTextureAlphaMod(states.texture->getSDLTexture(), vertices[i].color.a); SDL_RenderCopy(renderer, states.texture->getSDLTexture(), &src, &dest); } } else { for (size_t i = 0; i + 2 < vertices.size(); i += 3) { Vector2f pos[3]; for (int j = 0; j < 3; ++j) { pos[j] = combinedTransform.transformPoint(vertices[i + j].position); } SDL_SetRenderDrawColor(renderer, vertices[i].color.r, vertices[i].color.g, vertices[i].color.b, vertices[i].color.a); SDL_RenderDrawLine(renderer, static_cast(pos[0].x), static_cast(pos[0].y), static_cast(pos[1].x), static_cast(pos[1].y)); SDL_RenderDrawLine(renderer, static_cast(pos[1].x), static_cast(pos[1].y), static_cast(pos[2].x), static_cast(pos[2].y)); SDL_RenderDrawLine(renderer, static_cast(pos[2].x), static_cast(pos[2].y), static_cast(pos[0].x), static_cast(pos[0].y)); } } break; case PrimitiveType::TriangleStrip: if (states.texture) { for (size_t i = 0; i + 2 < vertices.size(); ++i) { Vector2f pos[3]; for (int j = 0; j < 3; ++j) { pos[j] = combinedTransform.transformPoint(vertices[i + j].position); } float minX = std::min(std::min(pos[0].x, pos[1].x), pos[2].x); float minY = std::min(std::min(pos[0].y, pos[1].y), pos[2].y); float maxX = std::max(std::max(pos[0].x, pos[1].x), pos[2].x); float maxY = std::max(std::max(pos[0].y, pos[1].y), pos[2].y); SDL_Rect dest = { static_cast(minX), static_cast(minY), static_cast(maxX - minX), static_cast(maxY - minY) }; SDL_Rect src = { static_cast(vertices[i].texCoords.x), static_cast(vertices[i].texCoords.y), static_cast(states.texture->getSize().x), static_cast(states.texture->getSize().y) }; SDL_SetTextureColorMod(states.texture->getSDLTexture(), vertices[i].color.r, vertices[i].color.g, vertices[i].color.b); SDL_SetTextureAlphaMod(states.texture->getSDLTexture(), vertices[i].color.a); SDL_RenderCopy(renderer, states.texture->getSDLTexture(), &src, &dest); } } else { for (size_t i = 0; i + 2 < vertices.size(); ++i) { Vector2f pos[3]; for (int j = 0; j < 3; ++j) { pos[j] = combinedTransform.transformPoint(vertices[i + j].position); } SDL_SetRenderDrawColor(renderer, vertices[i].color.r, vertices[i].color.g, vertices[i].color.b, vertices[i].color.a); SDL_RenderDrawLine(renderer, static_cast(pos[0].x), static_cast(pos[0].y), static_cast(pos[1].x), static_cast(pos[1].y)); SDL_RenderDrawLine(renderer, static_cast(pos[1].x), static_cast(pos[1].y), static_cast(pos[2].x), static_cast(pos[2].y)); SDL_RenderDrawLine(renderer, static_cast(pos[2].x), static_cast(pos[2].y), static_cast(pos[0].x), static_cast(pos[0].y)); } } break; case PrimitiveType::TriangleFan: if (states.texture) { for (size_t i = 1; i + 1 < vertices.size(); ++i) { Vector2f pos[3]; pos[0] = combinedTransform.transformPoint(vertices[0].position); pos[1] = combinedTransform.transformPoint(vertices[i].position); pos[2] = combinedTransform.transformPoint(vertices[i + 1].position); float minX = std::min(std::min(pos[0].x, pos[1].x), pos[2].x); float minY = std::min(std::min(pos[0].y, pos[1].y), pos[2].y); float maxX = std::max(std::max(pos[0].x, pos[1].x), pos[2].x); float maxY = std::max(std::max(pos[0].y, pos[1].y), pos[2].y); SDL_Rect dest = { static_cast(minX), static_cast(minY), static_cast(maxX - minX), static_cast(maxY - minY) }; SDL_Rect src = { static_cast(vertices[0].texCoords.x), static_cast(vertices[0].texCoords.y), static_cast(states.texture->getSize().x), static_cast(states.texture->getSize().y) }; SDL_SetTextureColorMod(states.texture->getSDLTexture(), vertices[0].color.r, vertices[0].color.g, vertices[0].color.b); SDL_SetTextureAlphaMod(states.texture->getSDLTexture(), vertices[0].color.a); SDL_RenderCopy(renderer, states.texture->getSDLTexture(), &src, &dest); } } else { for (size_t i = 1; i + 1 < vertices.size(); ++i) { Vector2f pos[3]; pos[0] = combinedTransform.transformPoint(vertices[0].position); pos[1] = combinedTransform.transformPoint(vertices[i].position); pos[2] = combinedTransform.transformPoint(vertices[i + 1].position); SDL_SetRenderDrawColor(renderer, vertices[0].color.r, vertices[0].color.g, vertices[0].color.b, vertices[0].color.a); SDL_RenderDrawLine(renderer, static_cast(pos[0].x), static_cast(pos[0].y), static_cast(pos[1].x), static_cast(pos[1].y)); SDL_RenderDrawLine(renderer, static_cast(pos[1].x), static_cast(pos[1].y), static_cast(pos[2].x), static_cast(pos[2].y)); SDL_RenderDrawLine(renderer, static_cast(pos[2].x), static_cast(pos[2].y), static_cast(pos[0].x), static_cast(pos[0].y)); } } break; case PrimitiveType::Quads: if (states.texture) { for (size_t i = 0; i + 3 < vertices.size(); i += 4) { Vector2f pos[4]; for (int j = 0; j < 4; ++j) { pos[j] = combinedTransform.transformPoint(vertices[i + j].position); } float minX = std::min(std::min(std::min(pos[0].x, pos[1].x), pos[2].x), pos[3].x); float minY = std::min(std::min(std::min(pos[0].y, pos[1].y), pos[2].y), pos[3].y); float maxX = std::max(std::max(std::max(pos[0].x, pos[1].x), pos[2].x), pos[3].x); float maxY = std::max(std::max(std::max(pos[0].y, pos[1].y), pos[2].y), pos[3].y); SDL_Rect dest = { static_cast(minX), static_cast(minY), static_cast(maxX - minX), static_cast(maxY - minY) }; SDL_Rect src = { static_cast(vertices[i].texCoords.x), static_cast(vertices[i].texCoords.y), static_cast(states.texture->getSize().x), static_cast(states.texture->getSize().y) }; SDL_SetTextureColorMod(states.texture->getSDLTexture(), vertices[i].color.r, vertices[i].color.g, vertices[i].color.b); SDL_SetTextureAlphaMod(states.texture->getSDLTexture(), vertices[i].color.a); SDL_RenderCopy(renderer, states.texture->getSDLTexture(), &src, &dest); } } else { for (size_t i = 0; i + 3 < vertices.size(); i += 4) { Vector2f pos[4]; for (int j = 0; j < 4; ++j) { pos[j] = combinedTransform.transformPoint(vertices[i + j].position); } SDL_SetRenderDrawColor(renderer, vertices[i].color.r, vertices[i].color.g, vertices[i].color.b, vertices[i].color.a); SDL_RenderDrawLine(renderer, static_cast(pos[0].x), static_cast(pos[0].y), static_cast(pos[1].x), static_cast(pos[1].y)); SDL_RenderDrawLine(renderer, static_cast(pos[1].x), static_cast(pos[1].y), static_cast(pos[2].x), static_cast(pos[2].y)); SDL_RenderDrawLine(renderer, static_cast(pos[2].x), static_cast(pos[2].y), static_cast(pos[3].x), static_cast(pos[3].y)); SDL_RenderDrawLine(renderer, static_cast(pos[3].x), static_cast(pos[3].y), static_cast(pos[0].x), static_cast(pos[0].y)); } } break; } } private: PrimitiveType primitiveType; std::vector vertices; Transform transform; }; // sf::RenderTexture class RenderTexture { public: RenderTexture() : texture(nullptr), renderer(nullptr), ownsRenderer(false) {} ~RenderTexture() { if (texture) SDL_DestroyTexture(texture); if (ownsRenderer && renderer) SDL_DestroyRenderer(renderer); } bool create(unsigned int width, unsigned int height) { if (!renderer) { renderer = SDL_CreateRenderer(nullptr, -1, SDL_RENDERER_ACCELERATED); if (!renderer) { throw std::runtime_error("Failed to create renderer: " + std::string(SDL_GetError())); } ownsRenderer = true; } texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, width, height); if (!texture) { if (ownsRenderer) SDL_DestroyRenderer(renderer); throw std::runtime_error("Failed to create render texture: " + std::string(SDL_GetError())); } textureWidth = width; textureHeight = height; internalTexture = Texture(texture, width, height); return true; } void clear(const Color& color = Color(0, 0, 0, 255)) { if (!texture) return; SDL_SetRenderTarget(renderer, texture); SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a); SDL_RenderClear(renderer); SDL_SetRenderTarget(renderer, nullptr); } void draw(const VertexArray& vertexArray, const RenderStates& states = RenderStates()) { if (!this->texture) return; SDL_SetRenderTarget(renderer, this->texture); vertexArray.draw(renderer, states); SDL_SetRenderTarget(renderer, nullptr); } void display() {} const Texture& getTexture() const { if (!texture) { throw std::runtime_error("No texture available"); } return internalTexture; } void setRenderer(SDL_Renderer* rend) { if (ownsRenderer && renderer) { SDL_DestroyRenderer(renderer); } renderer = rend; ownsRenderer = false; internalTexture = Texture(texture, textureWidth, textureHeight); } private: SDL_Texture* texture; SDL_Renderer* renderer; bool ownsRenderer; Texture internalTexture; int textureWidth = 0, textureHeight = 0; }; //--- class ViewManager { public: const View& getView() {return m_view;} const View& getDefaultView() {return m_view;} const Vector2u& getSize() const noexcept {return m_size;} protected: Vector2u m_size; View m_view; }; class Keyboard { public: enum Key { Unknown = -1, ///< Unhandled key A = SDLK_a, ///< The A key B = SDLK_b, ///< The B key C = SDLK_c, ///< The C key D = SDLK_d, ///< The D key E = SDLK_e, ///< The E key F = SDLK_f, ///< The F key G = SDLK_g, ///< The G key H = SDLK_h, ///< The H key I = SDLK_i, ///< The I key J = SDLK_j, ///< The J key K = SDLK_k, ///< The K key L = SDLK_l, ///< The L key M = SDLK_m, ///< The M key N = SDLK_n, ///< The N key O = SDLK_o, ///< The O key P = SDLK_p, ///< The P key Q = SDLK_q, ///< The Q key R = SDLK_r, ///< The R key S = SDLK_s, ///< The S key T = SDLK_t, ///< The T key U = SDLK_u, ///< The U key V = SDLK_v, ///< The V key W = SDLK_w, ///< The W key X = SDLK_x, ///< The X key Y = SDLK_y, ///< The Y key Z = SDLK_z, ///< The Z key Num0 = SDLK_0, ///< The 0 key Num1 = SDLK_1, ///< The 1 key Num2 = SDLK_2, ///< The 2 key Num3 = SDLK_3, ///< The 3 key Num4 = SDLK_4, ///< The 4 key Num5 = SDLK_5, ///< The 5 key Num6 = SDLK_6, ///< The 6 key Num7 = SDLK_7, ///< The 7 key Num8 = SDLK_8, ///< The 8 key Num9 = SDLK_9, ///< The 9 key Escape = #if !defined(__ANDROID__) SDLK_ESCAPE ///< The Escape key #else SDLK_AC_BACK ///< The Back key Android #endif , LControl = SDLK_LCTRL, ///< The left Control key LShift = SDLK_LSHIFT, ///< The left Shift key LAlt = SDLK_LALT, ///< The left Alt key //LSystem = SDLK_LEFT_SUPER, ///< The left OS specific key: window (Windows and Linux), apple (MacOS X), ... RControl = SDLK_RCTRL, ///< The right Control key RShift = SDLK_RSHIFT, ///< The right Shift key RAlt = SDLK_RALT, ///< The right Alt key //RSystem = SDLK_RIGHT_SUPER, ///< The right OS specific key: window (Windows and Linux), apple (MacOS X), ... Menu = SDLK_MENU, ///< The Menu key LBracket = SDLK_LEFTBRACKET, ///< The [ key RBracket = SDLK_RIGHTBRACKET, ///< The ] key Semicolon = SDLK_SEMICOLON, ///< The ; key Comma = SDLK_COMMA, ///< The , key Period = SDLK_PERIOD, ///< The . key Slash = SDLK_SLASH, ///< The / key Backslash = SDLK_BACKSLASH, ///< The \ key Equal = SDLK_EQUALS, ///< The = key Space = SDLK_SPACE, ///< The Space key Enter = SDLK_RETURN, ///< The Enter/Return keys Backspace = SDLK_BACKSPACE, ///< The Backspace key Tab = SDLK_TAB, ///< The Tabulation key PageUp = SDLK_PAGEUP, ///< The Page up key PageDown = SDLK_PAGEDOWN, ///< The Page down key End = SDLK_END, ///< The End key Home = SDLK_HOME, ///< The Home key Insert = SDLK_INSERT, ///< The Insert key Delete = SDLK_DELETE, ///< The Delete key //Add = SDLK_KP_ADD, ///< The + key //Subtract = SDLK_KP_SUBTRACT, ///< The - key (minus, usually from numpad) Multiply = SDLK_KP_MULTIPLY, ///< The * key Divide = SDLK_KP_DIVIDE, ///< The / key Left = SDLK_LEFT, ///< Left arrow Right = SDLK_RIGHT, ///< Right arrow Up = SDLK_UP, ///< Up arrow Down = SDLK_DOWN, ///< Down arrow Numpad0 = SDLK_0, ///< The numpad 0 key Numpad1 = SDLK_1, ///< The numpad 1 key Numpad2 = SDLK_2, ///< The numpad 2 key Numpad3 = SDLK_3, ///< The numpad 3 key Numpad4 = SDLK_4, ///< The numpad 4 key Numpad5 = SDLK_5, ///< The numpad 5 key Numpad6 = SDLK_6, ///< The numpad 6 key Numpad7 = SDLK_7, ///< The numpad 7 key Numpad8 = SDLK_8, ///< The numpad 8 key Numpad9 = SDLK_9, ///< The numpad 9 key F1 = SDLK_F1, ///< The F1 key F2 = SDLK_F2, ///< The F2 key F3 = SDLK_F3, ///< The F3 key F4 = SDLK_F4, ///< The F4 key F5 = SDLK_F5, ///< The F5 key F6 = SDLK_F6, ///< The F6 key F7 = SDLK_F7, ///< The F7 key F8 = SDLK_F8, ///< The F8 key F9 = SDLK_F9, ///< The F9 key F10 = SDLK_F10, ///< The F10 key F11 = SDLK_F11, ///< The F11 key F12 = SDLK_F12, ///< The F12 key F13 = SDLK_F13, ///< The F13 key F14 = SDLK_F14, ///< The F14 key F15 = SDLK_F15, ///< The F15 key Pause = SDLK_PAUSE, ///< The Pause key BackSpace = SDLK_BACKSPACE, ///< \deprecated Use Backspace instead BackSlash = SDLK_BACKSLASH, ///< \deprecated Use Backslash instead SemiColon = SDLK_SEMICOLON, ///< \deprecated Use Semicolon instead Return = Enter, ///< \deprecated Use Enter instead Underscore = SDLK_UNDERSCORE ///< \deprecated The Underscore (Only for SDL 2) }; static bool isKeyPressed(Key key); }; class Event { public: SDL_Event m_event; struct SizeEvent { unsigned int width; unsigned int height; }; struct MouseWheelEvent { int delta; ///< Number of ticks the wheel has moved (positive is up, negative is down) }; struct KeyEvent { int code; }; struct TouchEvent { unsigned int finger; int x; int y; }; enum EventType { Closed = SDL_QUIT, Resized = SDL_WINDOWEVENT_RESIZED, LostFocus = -2, GainedFocus = -1, MouseButtonPressed = SDL_MOUSEBUTTONDOWN, MouseButtonReleased = SDL_MOUSEBUTTONUP, MouseWheelMoved = SDL_MOUSEWHEEL, KeyPressed = SDL_KEYDOWN, KeyReleased = SDL_KEYUP, TouchBegan = SDL_FINGERDOWN, TouchMoved = SDL_FINGERMOTION, TouchEnded = SDL_FINGERUP, }; int type; union { SizeEvent size; KeyEvent key; TouchEvent touch; MouseWheelEvent mouseWheel; }; }; class VideoMode { public: VideoMode() {} VideoMode(unsigned int modeWidth, unsigned int modeHeight, unsigned int modeBitsPerPixel = 32): width(modeWidth), height(modeHeight), bitsPerPixel(modeBitsPerPixel) {} static VideoMode getDesktopMode(); unsigned int width; unsigned int height; unsigned int bitsPerPixel; }; class RenderWindow : public ViewManager { public: RenderWindow(): m_title(""), m_windowFrameLimit(1000 / 30) {} ~RenderWindow(); void create(VideoMode videoMode, const std::string& title, int style = Style::Default); void setFramerateLimit(int fps); void setSize(const Vector2u& size); void setTitle(const std::string& text); void setView(const View& view); void setPosition(Vector2i position); void setPosition(int x, int y); void setVerticalSyncEnabled(bool vsync); void setIcon(Uint32 width, Uint32 height, const Uint8* pixels); void clear(sf::Color const &color); void clear() {clear(sf::Color::Black);} void draw(SDLTexture &obj); void draw(Shape &obj) {obj.draw(m_view);} void draw(const VertexArray& vertexArray, const RenderStates& states = RenderStates()) { vertexArray.draw(is::IS_ENGINE_SDL_renderer, states); } void display(); void close() { is::IS_ENGINE_SDL_closeWindow = true; m_isOpen = false; } bool pollEvent(Event &event); bool waitEvent(Event &event); bool isOpen() const {return m_isOpen;} Vector2i getPosition() const; Vector2f mapPixelToCoords(const Vector2i& point, const View& view) const; private: SDL_Surface* m_SDLiconSurface = NULL; std::string m_title; unsigned int m_windowFrameLimit; int m_style; bool m_isOpen = true; float m_tempScreenXScale, m_tempScreenYScale; std::chrono::steady_clock::time_point m_timeSinceLastDisplay; ///< The timepoint at which Display() was last called }; typedef sf::RenderWindow Render; class SoundBuffer { public: static int SDL_sndChannel; SoundBuffer(); SoundBuffer(const std::string filename); ~SoundBuffer(); const std::string& getFileName() const noexcept {return m_filename;} bool loadFromFile(const std::string& filePath) { m_filename = filePath; return loadSound(m_filename); } /* void loadFromMemory() {functionNotSupported("SoundBuffer", "loadFromMemory", "loadSFMLSoundBuffer");} void loadFromStream() {functionNotSupported("SoundBuffer", "loadFromStream", "loadSFMLSoundBuffer");} void loadFromSamples() {functionNotSupported("SoundBuffer", "loadFromSamples", "loadSFMLSoundBuffer");} */ Mix_Chunk* getSDLChunk() const {return m_SDLsound;} int getSDLChannel() const {return m_channel;} private: Mix_Chunk *m_SDLsound = NULL; int m_channel; std::string m_filename = ""; bool loadSound(const std::string& filePath); void setChannelId(); }; class Sound : public SoundSource { public: Sound() : SoundSource() {} Sound(SoundBuffer& buffer) : SoundSource(), m_SDLsoundBuffer(&buffer) {} Status getStatus(); void play(); void setPitch(float speed); void pause() { Mix_Pause(m_SDLsoundBuffer->getSDLChannel()); m_status = Status::Paused; } void stop() { Mix_HaltChannel(m_SDLsoundBuffer->getSDLChannel()); m_status = Status::Stopped; } void setLoop(bool loop) { m_loop = loop; } void setVolume(float volume); void setBuffer(SoundBuffer &soundBuffer) { m_SDLsoundBuffer = &soundBuffer; } #if !defined(__ANDROID__) private: #else protected: #endif SoundBuffer *m_SDLsoundBuffer = nullptr; bool m_loop = false; }; class Music : public #if !defined(__ANDROID__) SoundSource #else Sound #endif { public: Music(); ~Music(); Status getStatus(); #if !defined(__ANDROID__) void play(); void setPitch(float speed); void pause() { Mix_PauseMusic(); m_status = Status::Paused; } void stop() { Mix_HaltMusic(); m_status = Status::Stopped; } void setLoop(bool loop) { m_loop = loop; } void setVolume(float volume); #endif bool openFromFile(const std::string& filePath); /* bool openFromMemory() {functionNotSupported("Music", "openFromMemory", "loadSFMLMusic");} bool openFromStream() {functionNotSupported("Music", "openFromStream", "loadSFMLMusic");} */ #if !defined(__ANDROID__) private: Mix_Music *m_music = NULL; bool m_loop = false; #endif }; class Mouse { public: enum Button { Left = SDL_BUTTON_LEFT, Right = SDL_BUTTON_RIGHT, Middle = SDL_BUTTON_MIDDLE, XButton1 = SDL_BUTTON_X1, XButton2 = SDL_BUTTON_X2 }; static bool isButtonPressed(Button button); static Vector2i getPosition(); static Vector2i getPosition(const RenderWindow& relativeTo); static void setPosition(const Vector2i& position); static void setPosition(const Vector2i& position, const RenderWindow& relativeTo); private: static Uint32 getSDLButtonState(); }; class Touch { public: // define array which will save different touch id static bool isDown(unsigned int finger); static Vector2i getPosition(unsigned int finger); static Vector2i getPosition(unsigned int finger, const RenderWindow& relativeTo); }; } #endif #endif // ISENGINESDLWRAPPER_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/islibconnect/isEngineVector2Wrapper.inl ================================================ #ifndef ISENGINEVECTOR2WRAPPER_INL_INCLUDED #define ISENGINEVECTOR2WRAPPER_INL_INCLUDED //////////////////////////////////////////////////////////// // Vector2 Operator //////////////////////////////////////////////////////////// template inline Vector2 operator -(const Vector2& right) {return Vector2(-right.x, -right.y);} template inline Vector2& operator +=(Vector2& left, const Vector2& right) { left.x += right.x; left.y += right.y; return left; } template inline Vector2& operator -=(Vector2& left, const Vector2& right) { left.x -= right.x; left.y -= right.y; return left; } template inline Vector2 operator +(const Vector2& left, const Vector2& right) {return Vector2(left.x + right.x, left.y + right.y);} template inline Vector2 operator -(const Vector2& left, const Vector2& right) {return Vector2(left.x - right.x, left.y - right.y);} template inline Vector2 operator *(const Vector2& left, T right) {return Vector2(left.x * right, left.y * right);} template inline Vector2 operator *(T left, const Vector2& right) {return Vector2(right.x * left, right.y * left);} template inline Vector2& operator *=(Vector2& left, T right) { left.x *= right; left.y *= right; return left; } template inline Vector2 operator /(const Vector2& left, T right) {return Vector2(left.x / right, left.y / right);} template inline Vector2& operator /=(Vector2& left, T right) { left.x /= right; left.y /= right; return left; } template inline bool operator ==(const Vector2& left, const Vector2& right) {return (left.x == right.x) && (left.y == right.y);} template inline bool operator !=(const Vector2& left, const Vector2& right) {return (left.x != right.x) || (left.y != right.y);} #endif // ISENGINEVECTOR2WRAPPER_INL_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/islibconnect/isEngineWrapper.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef ISENGINEWRAPPER_H_INCLUDED #define ISENGINEWRAPPER_H_INCLUDED #include #include #include #if (defined(__ANDROID__) || defined(IS_ENGINE_HTML_5) || defined(IS_ENGINE_SWITCH)) #define IS_ENGINE_SDL_2 #endif namespace is { /// Allows to close application inline void closeApplication() { std::terminate(); } /// Return distance between two points (x1, y1) and (x2, y2) inline float pointDistance(float x1, float y1, float x2, float y2) { float X = x1 - x2; float Y = y1 - y2; return sqrt(X * X + Y * Y); } template void setVector2(T1 &v, T2 x, T2 y) { v.x = x; v.y = y; }; /// Clear render template void clear(T1 &render, T2 const &color) { render.clear(color); } template void clear(T1 &render) { render.clear(); } /// Display Render template void display(T &render) { render.display(); } } #if defined(IS_ENGINE_SDL_2) namespace sf { /* // Allows to display an error message in the console when there is not a similar function of SFML in SMK inline void functionNotSupported(const std::string &className, const std::string &functionName, const std::string &suitableFunction = "") { std::cout << "\n" + ((className != "") ? className + "::" : "") + functionName + "() is not supported on the SMK library\n"; if (suitableFunction != "") std::cout << "Use this function instead is::" + suitableFunction + "()\n"; is::closeApplication(); } */ typedef char Int8; typedef short Int16; typedef int Int32; typedef long Int64; typedef unsigned char Uint8; typedef unsigned short Uint16; typedef unsigned int Uint32; typedef unsigned long Uint64; template class Vector2 { public: Vector2() {} Vector2(T X, T Y) : x(X), y(Y) {} T x; T y; }; #include "isEngineVector2Wrapper.inl" typedef Vector2 Vector2i; typedef Vector2 Vector2u; typedef Vector2 Vector2f; class Rect { public: int left; int top; int width; int height; Rect() : left(0), top(0), width(0), height(0) {} Rect(int _left, int _top, int _width, int _height) : left(_left), top(_top), width(_width), height(_height) {} bool intersects(Rect const &rec) const; bool intersects(Rect const &rec1, Rect const &rec2) const; template bool contains(T x, T y) const { T minX = static_cast(left); T maxX = static_cast(left + width); T minY = static_cast(top); T maxY = static_cast(top + height); return (x >= minX) && (x < maxX) && (y >= minY) && (y < maxY); } template bool contains(const Vector2& point) const { return contains(point.x, point.y); } }; typedef Rect IntRect; typedef Rect FloatRect; //class String : public std::string {}; Rect functionGetGlobalBounds(const Vector2f &position, const Vector2f &origin, const Vector2f &size); //Color functionGetColor(Color &color); class Time { public: Time() : m_microseconds(0.f) {}; float asSeconds() const {return m_microseconds / 1000000.f;} Int32 asMilliseconds() const {return static_cast(m_microseconds / 1000);} Int64 asMicroseconds() const {return m_microseconds;} static const Time Zero; bool operator>(const Time& time) const {return asMicroseconds() > time.asMicroseconds();} bool operator<(const Time& time) const {return asMicroseconds() < time.asMicroseconds();} private: friend Time seconds(float); friend Time milliseconds(Int32); friend Time microseconds(Int64); explicit Time(Int64 microseconds) : m_microseconds(microseconds){} Int64 m_microseconds; }; Time seconds(float amount); Time milliseconds(Int32 amount); Time microseconds(Int64 amount); class Clock { public: Clock(); const Time getElapsedTime(); Time restart(); private: Time m_startTime; }; class SoundSource { public: enum Status { Stopped, Playing, Paused }; SoundSource() : m_status(Stopped) {} protected: Status m_status = Stopped; }; enum Style { None, Titlebar, Resize, Close, Fullscreen, Default }; } #endif #endif // ISENGINEWRAPPER_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/islibconnect/isLibConnect.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef ISLIBCONNECT_H_INCLUDED #define ISLIBCONNECT_H_INCLUDED #if (defined(IS_ENGINE_SDL_2) || defined(IS_ENGINE_HTML_5) || defined(__ANDROID__) || defined(IS_ENGINE_SWITCH)) #include "isEngineSDLWrapper.h" #else #include #include #include #include #include #include "isEngineWrapper.h" namespace is { static bool IS_ENGINE_MOBILE_OS( #if defined(__ANDROID__) true #else false #endif ); } #endif #if !defined(IS_ENGINE_SDL_2) #define IS_ENGINE_SFML #endif namespace is { #if defined(IS_ENGINE_SDL_2) /// Draw on render inline void draw(sf::RenderWindow &render, sf::SDLTexture &obj) {render.draw(obj);} inline void draw(sf::RenderWindow &render, sf::SDLTexture *obj) {render.draw(*obj);} inline void draw(sf::RenderWindow &render, sf::Shape &obj) {render.draw(obj);} inline void draw(sf::RenderWindow &render, sf::Shape *obj) {render.draw(*obj);} #else template void draw(T1 &render, T2 &obj) {render.draw(obj);} template void draw(T1 &render, T2 *obj) {render.draw(&obj);} #endif typedef sf::RenderWindow Render; } #endif // ISLIBCONNECT_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/sound/GSM.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef GSM_H_INCLUDED #define GSM_H_INCLUDED #include "../sound/GameSound.h" #include "../sound/GameMusic.h" namespace is { //////////////////////////////////////////////////////////// /// Class for manage SFML Sound and Music without using an /// SFML object //////////////////////////////////////////////////////////// class GSM { public: /// Game Sound container std::vector> m_GSMsound; //#if !defined(__ANDROID__) /// Game Music container std::vector> m_GSMmusic; //#endif ////////////////////////////////////////////////////// /// \brief Allows to add SFML Sound in container /// /// \param name of sound which will be used to identify /// it in the container in order to be able to access it /// \param filePath path of the sound file to add ////////////////////////////////////////////////////// virtual void GSMaddSound(const std::string& name, const std::string& filePath) { if (!soundFileExists(filePath)) { auto obj = std::make_shared(name, filePath); m_GSMsound.push_back(obj); } else is::showLog("WARNING: <" + name + "> sound has already been added!"); } ////////////////////////////////////////////////////// /// \brief Allows to add existing sound in container /// /// \param sound object ////////////////////////////////////////////////////// void GSMaddSoundObject(std::shared_ptr sound, bool showError = true) { if (!soundFileExists(sound->getFilePath())) m_GSMsound.push_back(sound); else { if (showError) is::showLog("WARNING: <" + sound->getName() + "> sound has already been added!"); } } ////////////////////////////////////////////////////// /// \brief Allows to add SFML Music in container /// /// \param name of music which will be used to identify /// it in the container in order to be able to access it /// \param filePath path of the music file to add ////////////////////////////////////////////////////// virtual void GSMaddMusic(const std::string& name, const std::string& filePath) { //#if defined(__ANDROID__) // GSMaddSound(name, filePath); //#else if (!musicFileExists(filePath)) { auto obj = std::make_shared(name, filePath); m_GSMmusic.push_back(obj); } else is::showLog("WARNING: <" + name + "> music has already been added!"); //#endif } ////////////////////////////////////////////////////// /// \brief Allows to add existing music in container /// /// \param music object ////////////////////////////////////////////////////// void GSMaddMusicObject(std::shared_ptr< //#if defined(__ANDROID__) // GameSound //#else GameMusic //#endif >music, bool showError = true) { //#if defined(__ANDROID__) // GSMaddSoundObject(music); //#else if (!musicFileExists(music->getFilePath())) m_GSMmusic.push_back(music); else { if (showError) is::showLog("WARNING: <" + music->getName() + "> music has already been added!"); } //#endif } /// Allows to set sound loop virtual void GSMsetSoundLoop(const std::string& name, bool loop) { WITH(m_GSMsound.size()) { if (m_GSMsound[_I].get() != nullptr) { if (m_GSMsound[_I]->getName() == name && m_GSMsound[_I]->getFileIsLoaded()) { m_GSMsound[_I]->getSound().setLoop(loop); return; } } } is::showLog("ERROR: Can't loop <" + name + "> sound because sound does not exist!"); } /// Allows to set music loop virtual void GSMsetMusicLoop(const std::string& name, bool loop) { //#if defined(__ANDROID__) // GSMsetSoundLoop(name, loop); //#else WITH(m_GSMmusic.size()) { if (m_GSMmusic[_I].get() != nullptr) { if (m_GSMmusic[_I]->getName() == name && m_GSMmusic[_I]->getFileIsLoaded()) { m_GSMmusic[_I]->getMusic().setLoop(loop); return; } } } is::showLog("ERROR: Can't loop <" + name + "> music because music does not exist!"); //#endif } /// Allows to get sound in container by his name virtual sf::Sound* GSMgetSound(const std::string& name, bool showError = true) { WITH(m_GSMsound.size()) { if (m_GSMsound[_I].get() != nullptr) { if (m_GSMsound[_I]->getName() == name && m_GSMsound[_I]->getFileIsLoaded()) { return &m_GSMsound[_I]->getSound(); } } } if (showError) is::showLog("ERROR: <" + name + "> sound does not exist!"); return nullptr; } /// Allows to get music in container by his name virtual //#if defined(__ANDROID__) // sf::Sound* //#else sf::Music* //#endif GSMgetMusic(const std::string& name, bool showError = true) { WITH( //#if defined(__ANDROID__) // m_GSMsound.size() //#else m_GSMmusic.size() //#endif ) { if ( //#if defined(__ANDROID__) // m_GSMsound[_I].get() != nullptr //#else m_GSMmusic[_I].get() != nullptr //#endif ) { if ( //#if defined(__ANDROID__) // m_GSMsound[_I]->getName() == name && m_GSMsound[_I]->getFileIsLoaded() //#else m_GSMmusic[_I]->getName() == name && m_GSMmusic[_I]->getFileIsLoaded() //#endif ) { return //#if defined(__ANDROID__) // &m_GSMsound[_I]->getSound(); //#else &m_GSMmusic[_I]->getMusic(); //#endif } } } if (showError) is::showLog("ERROR: <" + name + "> music does not exist!"); return nullptr; } /// Allows to pause sound in container by his name virtual void GSMpauseSound(const std::string& name) { bool soundExist(false); WITH(m_GSMsound.size()) { if (m_GSMsound[_I].get() != nullptr) { if (m_GSMsound[_I]->getName() == name) { soundExist = true; if (m_GSMsound[_I]->getFileIsLoaded()) { if (is::checkSFMLSndState(m_GSMsound[_I]->getSound(), is::SFMLSndStatus::Playing)) m_GSMsound[_I]->getSound().pause(); } else is::showLog("ERROR: Can't pause <" + name + "> sound!"); break; } } } if (!soundExist) is::showLog("ERROR: Can't pause <" + name + "> sound because sound does not exist!"); } /// Allows to stop sound in container by his name virtual void GSMstopSound(const std::string& name) { bool soundExist(false); WITH(m_GSMsound.size()) { if (m_GSMsound[_I].get() != nullptr) { if (m_GSMsound[_I]->getName() == name) { soundExist = true; if (m_GSMsound[_I]->getFileIsLoaded()) { if (is::checkSFMLSndState(m_GSMsound[_I]->getSound(), is::SFMLSndStatus::Playing)) m_GSMsound[_I]->getSound().stop(); } else is::showLog("ERROR: Can't stop <" + name + "> sound!"); break; } } } if (!soundExist) is::showLog("ERROR: Can't stop <" + name + "> sound because sound does not exist!"); } /// Allows to pause music in container by his name virtual void GSMpauseMusic(const std::string& name) { // #if defined(__ANDROID__) // GSMpauseSound(name); // #else bool musicExist(false); WITH(m_GSMmusic.size()) { if (m_GSMmusic[_I].get() != nullptr) { if (m_GSMmusic[_I]->getName() == name) { musicExist = true; if (m_GSMmusic[_I]->getFileIsLoaded()) { if (is::checkSFMLSndState(m_GSMmusic[_I]->getMusic(), is::SFMLSndStatus::Playing)) m_GSMmusic[_I]->getMusic().pause(); } else is::showLog("ERROR: Can't pause <" + name + "> music!"); break; } } } if (!musicExist) is::showLog("ERROR: Can't pause <" + name + "> music because music does not exist!"); // #endif } /// Allows to stop music in container by his name virtual void GSMstopMusic(const std::string& name) { // #if defined(__ANDROID__) // GSMstopSound(name); // #else bool musicExist(false); WITH(m_GSMmusic.size()) { if (m_GSMmusic[_I].get() != nullptr) { if (m_GSMmusic[_I]->getName() == name) { musicExist = true; if (m_GSMmusic[_I]->getFileIsLoaded()) { if (is::checkSFMLSndState(m_GSMmusic[_I]->getMusic(), is::SFMLSndStatus::Playing)) m_GSMmusic[_I]->getMusic().stop(); } else is::showLog("ERROR: Can't stop <" + name + "> music!"); break; } } } if (!musicExist) is::showLog("ERROR: Can't stop <" + name + "> music because music does not exist!"); // #endif } /// Allows to delete sound in container by his name virtual void GSMdeleteSound(const std::string& name) { int soundId(-1); WITH(m_GSMsound.size()) { if (m_GSMsound[_I].get() != nullptr) { if (m_GSMsound[_I]->getName() == name) { soundId = _I; break; } } } if (soundId == -1) is::showLog("ERROR: Can't delete <" + name + "> sound because sound does not exist!"); else { m_GSMsound[soundId].reset(); m_GSMsound[soundId] = nullptr; } } /// Allows to delete music in container by his name virtual void GSMdeleteMusic(const std::string& name) { // #if defined(__ANDROID__) // GSMdeleteSound(name); // #else int musicId(-1); WITH(m_GSMmusic.size()) { if (m_GSMmusic[_I].get() != nullptr) { if (m_GSMmusic[_I]->getName() == name) { musicId = _I; break; } } } if (musicId == -1) is::showLog("ERROR: Can't delete <" + name + "> music because music does not exist!"); else { m_GSMmusic[musicId].reset(); m_GSMmusic[musicId] = nullptr; } // #endif } //////////////////////////////////////////////////////////// /// These methods below have the same role as those above. /// The difference here is that their name starting with GSM /// is replaced by GRM (Game Resource Manager). //////////////////////////////////////////////////////////// ////////////////////////////////////////////////////// /// \brief Allows to add SFML Sound in container /// /// \param name of sound which will be used to identify /// it in the container in order to be able to access it /// \param filePath path of the sound file to add ////////////////////////////////////////////////////// virtual void GRMaddSound(const std::string& name, const std::string& filePath) { GSMaddSound(name, filePath); } ////////////////////////////////////////////////////// /// \brief Allows to add existing sound in container /// /// \param sound object ////////////////////////////////////////////////////// void GRMaddSoundObject(std::shared_ptr sound, bool showError = true) { GSMaddSoundObject(sound, showError); } ////////////////////////////////////////////////////// /// \brief Allows to add SFML Music in container /// /// \param name of music which will be used to identify /// it in the container in order to be able to access it /// \param filePath path of the music file to add ////////////////////////////////////////////////////// virtual void GRMaddMusic(const std::string& name, const std::string& filePath) { //#if defined(__ANDROID__) // GSMaddSound(name, filePath); //#else GSMaddMusic(name, filePath); //#endif } ////////////////////////////////////////////////////// /// \brief Allows to add existing music in container /// /// \param music object ////////////////////////////////////////////////////// void GRMaddMusicObject(std::shared_ptr< //#if defined(__ANDROID__) // GameSound //#else GameMusic //#endif >music, bool showError = true) { //#if defined(__ANDROID__) // GSMaddSoundObject(music, showError); //#else GSMaddMusicObject(music, showError); //#endif } /// Allows to set sound loop virtual void GRMsetSoundLoop(const std::string& name, bool loop) { GSMsetSoundLoop(name, loop); } /// Allows to set music loop virtual void GRMsetMusicLoop(const std::string& name, bool loop) { //#if defined(__ANDROID__) // GSMsetSoundLoop(name, loop); //#else GSMsetMusicLoop(name, loop); //#endif } /// Allows to get sound in container by his name virtual sf::Sound* GRMgetSound(const std::string& name, bool showError = true) { return GSMgetSound(name, showError); } /// Allows to get music in container by his name virtual //#if defined(__ANDROID__) // sf::Sound* //#else sf::Music* //#endif GRMgetMusic(const std::string& name, bool showError = true) { return //#if defined(__ANDROID__) // GSMgetSound(name, showError); //#else GSMgetMusic(name, showError); //#endif } /// Allows to pause sound in container by his name virtual void GRMpauseSound(const std::string& name) { GSMpauseSound(name); } /// Allows to stop sound in container by his name virtual void GRMstopSound(const std::string& name) { GSMstopSound(name); } /// Allows to pause music in container by his name virtual void GRMpauseMusic(const std::string& name) { // #if defined(__ANDROID__) // GSMpauseSound(name); // #else GSMpauseMusic(name); // #endif } /// Allows to stop music in container by his name virtual void GRMstopMusic(const std::string& name) { // #if defined(__ANDROID__) // GSMstopSound(name); // #else GSMstopMusic(name); // #endif } /// Allows to delete sound in container by his name virtual void GRMdeleteSound(const std::string& name) { GSMdeleteSound(name); } /// Allows to delete music in container by his name virtual void GRMdeleteMusic(const std::string& name) { // #if defined(__ANDROID__) // GSMdeleteSound(name); // #else GSMdeleteMusic(name); // #endif } private: bool soundFileExists(const std::string& filePath) const { WITH(m_GSMsound.size()) { if (m_GSMsound[_I].get() != nullptr) { if (m_GSMsound[_I]->getFilePath() == filePath) return true; } } return false; } //#if !defined(__ANDROID__) bool musicFileExists(const std::string& filePath) const { WITH(m_GSMmusic.size()) { if (m_GSMmusic[_I].get() != nullptr) { if (m_GSMmusic[_I]->getFilePath() == filePath) return true; } } return false; } //#endif }; } #endif // GSM_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/sound/GameMusic.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef GAMEMUSIC_H_INCLUDED #define GAMEMUSIC_H_INCLUDED #include "../islibconnect/isLibConnect.h" #include "../entity/parents/Name.h" #include "../entity/parents/FilePath.h" namespace is { //////////////////////////////////////////////////////////// /// Class for manage SFML Music //////////////////////////////////////////////////////////// class GameMusic : public is::Name, public is::FilePath { public: GameMusic(const std::string& musicName, const std::string& filePath): Name(musicName), FilePath(filePath) { if (m_music.openFromFile(m_strFilePath)) m_fileIsLoaded = true; //else showLog("ERROR: Can't load music : " + filePath); } virtual ~GameMusic() {} void loadResources(const std::string&filePath) { if (m_music.openFromFile(filePath)) { m_strFilePath = filePath; m_fileIsLoaded = true; } else { m_fileIsLoaded = false; //showLog("ERROR: Can't load music : " + filePath); } } /// Return music object sf::Music& getMusic() { return m_music; } private: sf::Music m_music; }; } #endif // GAMEMUSIC_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngine/system/sound/GameSound.h ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef GAMESOUND_H_INCLUDED #define GAMESOUND_H_INCLUDED #include "../islibconnect/isLibConnect.h" #include "../entity/parents/Name.h" #include "../entity/parents/FilePath.h" namespace is { //////////////////////////////////////////////////////////// /// Class for manage SFML Sound //////////////////////////////////////////////////////////// class GameSound : public is::Name, public is::FilePath { public: GameSound(const std::string& soundName, const std::string& filePath): Name(soundName), FilePath(filePath) { if (m_sb.loadFromFile(m_strFilePath)) { m_snd.setBuffer(m_sb); m_fileIsLoaded = true; } //else showLog("ERROR: Can't load sound : " + filePath); } virtual ~GameSound() {} void loadResources(const std::string& filePath) { if (m_sb.loadFromFile(filePath)) { m_strFilePath = filePath; m_snd.setBuffer(m_sb); m_fileIsLoaded = true; } else { m_fileIsLoaded = false; //showLog("ERROR: Can't load sound : " + filePath); } } /// Return sound buffer sf::SoundBuffer& getSoundBuffer() {return m_sb;} /// Return sound object sf::Sound& getSound() {return m_snd;} private: sf::SoundBuffer m_sb; sf::Sound m_snd; }; } #endif // GAMESOUND_H_INCLUDED ================================================ FILE: app/src/main/cpp/isEngineSDLWrapper.cpp ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2025 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/system/islibconnect/isEngineSDLWrapper.h" #if defined(IS_ENGINE_SDL_2) namespace is { SDL_Window *IS_ENGINE_SDL_window = NULL; SDL_Renderer *IS_ENGINE_SDL_renderer = NULL; SDL_DisplayMode IS_ENGINE_SDL_displayMode; bool IS_ENGINE_MOBILE_OS(false); float IS_ENGINE_SDL_screenXScale(1.f); float IS_ENGINE_SDL_screenYScale(1.f); short IS_ENGINE_SDL_channel[IS_ENGINE_SDL_CHANNEL_MAX] = {-1}; TouchData IS_ENGINE_SDL_touchData[IS_ENGINE_SDL_TOUCH_ID_COUNT_MAX]; bool IS_ENGINE_SDL_enableFINGERMOTION = true; short IS_ENGINE_SDL_touchIdLast = 0; short IS_ENGINE_SDL_touchIdCount = 0; std::vector IS_ENGINE_SDL_AUTO_GENERATE_FONT; Uint8 IS_ENGINE_SDL_buttonState[NUM_BUTTONS] = {0}; SDL_GameController *IS_ENGINE_SDL_controller = NULL; void updateButtonState() { for (int i = 0; i < NUM_BUTTONS; ++i) { IS_ENGINE_SDL_buttonState[i] = SDL_GameControllerGetButton(IS_ENGINE_SDL_controller, (SDL_GameControllerButton)i); } } const Uint8* getControllerButtonState(int *numButtons) { if (numButtons) *numButtons = NUM_BUTTONS; return IS_ENGINE_SDL_buttonState; } bool SDL2initLib() { if (IS_ENGINE_SDL_window == NULL || IS_ENGINE_SDL_renderer == NULL) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "init Windows / Renderer : %s\n", SDL_GetError()); return false; } if (SDL_GetCurrentDisplayMode( 0, &IS_ENGINE_SDL_displayMode) != 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Display Mode : %s\n", SDL_GetError()); } SDL_SetRenderDrawBlendMode(IS_ENGINE_SDL_renderer, SDL_BLENDMODE_BLEND); int imgFlags = IMG_INIT_PNG; if (!(IMG_Init(imgFlags) & imgFlags)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "init Image : %s\n", IMG_GetError()); return false; } if (TTF_Init() < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "init Font : %s\n", TTF_GetError()); return false; } // On Android, the use of .wav type music files is not yet supported #if !defined(__ANDROID__) int audioFlags = MIX_INIT_OGG; if ((Mix_Init(audioFlags) & audioFlags) != audioFlags) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "init Mix : %s\n", Mix_GetError()); return false; } #endif const int frequency = #if defined(IS_ENGINE_HTML_5) EM_ASM_INT_V({ var context; try { context = new AudioContext(); } catch(e) { context = new webkitAudioContext(); } return context.sampleRate; }); // Check if it is mobile platform int tempBoolToInt = EM_ASM_INT ( let ua = navigator.userAgent ; if (/(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i.test(ua)) { return 1; } else if (/Mobile|Android|iP(hone|od)|IEMobile|BlackBerry|Kindle|Silk-Accelerated|(hpw|web)OS|Opera M(obi|ini)/.test(ua)) { return 1; } return 0; ); IS_ENGINE_MOBILE_OS = (tempBoolToInt == 1) ? true : false; #elif defined(IS_ENGINE_SWITCH) 48000; // The Switch has the same features as a mobile (smartphone), so we define it as a mobile. IS_ENGINE_MOBILE_OS = true; #else 22050; //44100; #endif #if defined(__SWITCH__) SDL_InitSubSystem(SDL_INIT_AUDIO); #endif if (Mix_OpenAudio(frequency, #if defined(__SWITCH__) AUDIO_S16 #else MIX_DEFAULT_FORMAT #endif , 2, 4096) == -1) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "init OpenAudio : %s\n", Mix_GetError()); } Mix_AllocateChannels(IS_ENGINE_SDL_CHANNEL_MAX); return true; } void SDL2freeLib() { Mix_CloseAudio(); Mix_Quit(); SDL_DestroyWindow(IS_ENGINE_SDL_window); is::IS_ENGINE_SDL_window = NULL; SDL_DestroyRenderer(IS_ENGINE_SDL_renderer); is::IS_ENGINE_SDL_renderer = NULL; IMG_Quit(); for (unsigned int _I(0); _I < IS_ENGINE_SDL_AUTO_GENERATE_FONT.size(); _I++) { delete IS_ENGINE_SDL_AUTO_GENERATE_FONT[_I]; IS_ENGINE_SDL_AUTO_GENERATE_FONT[_I] = 0; } TTF_Quit(); #ifdef __SWITCH__ romfsExit(); #endif } } namespace sf { int SoundBuffer::SDL_sndChannel = 0; // Represents the channel of each sound sf::Color Color::White = sf::Color(255, 255, 255, 255); sf::Color Color::Black = sf::Color(0, 0, 0, 255); sf::Color Color::Grey = sf::Color(127, 127, 127, 255); sf::Color Color::Red = sf::Color(255, 0, 0, 255); sf::Color Color::Green = sf::Color(0, 255, 0, 255); sf::Color Color::Blue = sf::Color(0, 0, 255, 255); sf::Color Color::Yellow = sf::Color(255, 255, 0, 255); sf::Color Color::Magenta = sf::Color(255, 0, 255, 255); sf::Color Color::Cyan = sf::Color(0, 255, 255, 255); sf::Color Color::Transparent = sf::Color(0, 0, 0, 0); Texture::~Texture() { if (m_SDLsurface != NULL) { SDL_FreeSurface(m_SDLsurface); m_SDLsurface = NULL; } if (m_texture) SDL_DestroyTexture(m_texture); } bool Texture::loadSurface(const std::string& filePath, bool useWithVertices) { m_filename = filePath; m_SDLsurface = IMG_Load(m_filename.c_str()); if (m_SDLsurface != NULL) { m_size.x = m_SDLsurface->w; m_size.y = m_SDLsurface->h; if (useWithVertices) { m_texture = SDL_CreateTextureFromSurface(is::IS_ENGINE_SDL_renderer, m_SDLsurface); if (!m_texture) { throw std::runtime_error("Failed to create texture: " + std::string(SDL_GetError())); } } } else { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Texture : %s\n \"%s\"", SDL_GetError(), m_filename.c_str()); return false; } return true; } Font::~Font() { if (m_SDLfont != NULL) { TTF_CloseFont(m_SDLfont); m_SDLfont = NULL; } } bool Font::loadFont(const std::string& filename) { m_filename = filename; m_SDLfont = TTF_OpenFont(m_filename.c_str(), m_size); if (m_SDLfont == NULL) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Font : %s \"%s\"\n", TTF_GetError(), filename.c_str()); return false; } return true; } Transformable::Transformable() { is::setVector2(m_size, 0.f, 0.f); is::setVector2(m_scale, 1.f, 1.f); is::setVector2(m_origin, 0.f, 0.f); } Transformable::Transformable(Texture &texture) : m_texture(&(texture)) { is::setVector2(m_size, m_texture->getSize().x, m_texture->getSize().y); is::setVector2(m_scale, 1.f, 1.f); is::setVector2(m_origin, 0.f, 0.f); } void Transformable::setRotation(float angle) { m_rotation = static_cast(fmod(angle, 360)); if (m_rotation < 0) m_rotation += 360.f; } void Transformable::setColor(int r, int g, int b, int a) { m_color.r = r; m_color.g = g; m_color.b = b; if (a >= 0 && a <= 255) m_color.a = a; } const SDL_Color& Transformable::getSDLColor(bool getAlpha) { m_SDLcolor.r = m_color.r; m_SDLcolor.g = m_color.g; m_SDLcolor.b = m_color.b; m_SDLcolor.a = ((getAlpha) ? m_color.a : 0); return m_SDLcolor; } SDLTexture::~SDLTexture() { if (m_SDLtexture != NULL) { SDL_DestroyTexture(m_SDLtexture); m_SDLtexture = NULL; } if (m_SDLoutlineTexture != NULL) { SDL_DestroyTexture(m_SDLoutlineTexture); m_SDLoutlineTexture = NULL; } } void SDLTexture::setTextureRect(IntRect rec) { m_textureRec.left = rec.left; m_textureRec.top = rec.top; m_textureRec.width = rec.width; m_textureRec.height = rec.height; is::setVector2(m_size, m_textureRec.width, m_textureRec.height); } void Sprite::setTexture(sf::Texture& texture) { m_texture = &texture; setSDLTexture(); } void Sprite::setSDLTexture() { if (m_texture->getSDLSurface() != NULL) { if (m_SDLtexture != NULL) { SDL_DestroyTexture(m_SDLtexture); m_SDLtexture = NULL; } m_SDLtexture = SDL_CreateTextureFromSurface(is::IS_ENGINE_SDL_renderer, m_texture->getSDLSurface()); setTextureRect({0, 0, (int)m_texture->getSize().x, (int)m_texture->getSize().y}); } } Image::~Image() { if (m_texture != nullptr) { delete m_texture; m_texture = nullptr; } } bool Image::loadFromFile(const std::string& filename) { if (m_texture != nullptr) { delete m_texture; m_texture = nullptr; } m_texture = new Texture(filename); if (m_texture == nullptr) return false; if (m_texture->getSDLSurface() != NULL) { m_SDLtexture = SDL_CreateTextureFromSurface(is::IS_ENGINE_SDL_renderer, m_texture->getSDLSurface()); setSize(m_texture->getSize().x, m_texture->getSize().y); } return true; } const Uint8* Image::getPixelsPtr() const { int pitch; void *pixels; SDL_LockTexture(m_SDLtexture, NULL, &pixels, &pitch); Uint8 *upixels = (Uint8*) pixels; memcpy(pixels, upixels, (pitch / 4) * m_texture->getSize().y); SDL_UnlockTexture(m_SDLtexture); return upixels; } Text::Text(sf::Font& font) : SDLTexture() { m_SDLTextureType = IS_ENGINE_SDL_TEXT; m_font = &font; m_characterSize = m_font->getSize(); m_style = m_font->m_SDLFontStyle; m_outlineFont = m_font; } Text::Text(sf::Font& font, const std::string& text) : SDLTexture() { m_SDLTextureType = IS_ENGINE_SDL_TEXT; m_font = &font; m_characterSize = m_font->getSize(); m_style = m_font->m_SDLFontStyle; m_outlineFont = m_font; setObjectText(text); } Text::Text(sf::Font& font, const std::wstring& text): SDLTexture() { m_SDLTextureType = IS_ENGINE_SDL_TEXT; m_font = &font; m_characterSize = m_font->getSize(); m_style = m_font->m_SDLFontStyle; m_outlineFont = m_font; setObjectText(text); } Text::~Text() { if (m_SDLtexture != NULL) { SDL_DestroyTexture(m_SDLtexture); m_SDLtexture = NULL; } if (m_SDLtext != nullptr) { delete m_SDLtext; m_SDLtext = nullptr; } } void Text::setFont(sf::Font &font) { m_font = &font; m_characterSize = m_font->getSize(); m_style = m_font->m_SDLFontStyle; if (m_outlineFont == nullptr) m_outlineFont = m_font; setSDLText(); } void Text::setString(const std::wstring& text) { setObjectText(text); } void Text::setString(const wchar_t& text) { m_tempWstring = text; setObjectText(m_tempWstring); } void Text::setString(const std::string& text) { setObjectText(text); } void Text::setString(const char& text) { m_tempString = text; setObjectText(m_tempString); } void Text::setColor(int r, int g, int b, int a) { if (a >= 0 && a <= 255) m_color.a = a; if (m_color.r != r || m_color.g != g || m_color.b != b) { m_color.r = r; m_color.g = g; m_color.b = b; setSDLText(); } } void Text::setOrigin(float x, float y) { is::setVector2(m_origin, x, y); } void Text::setCharacterSize(int size) { if (m_characterSize != size) { m_characterSize = size; setSDLText(); } } void Text::setStyle(Uint32 style) { if (m_style != style) { m_style = style; setSDLText(); } } void Text::setOutlineColor(const Color& color) { if (m_outlineColor.r != color.r || m_outlineColor.g != color.g || m_outlineColor.b != color.b || m_outlineColor.a != color.a) { m_outlineColor = color; setSDLText(); } } void Text::setOutlineThickness(float thickness) { if (m_outlineThickness != thickness) { m_outlineThickness = thickness; setSDLText(); } } void Text::setObjectText(const std::string& text) { if (text != m_string) { m_string = text; if (m_SDLtext != nullptr) { delete m_SDLtext; m_SDLtext = nullptr; } m_currentCharSize = text.length() + 1; m_SDLtext = new char[m_currentCharSize]; strcpy(m_SDLtext, text.c_str()); m_SDLtext[m_currentCharSize - 1] = '\0'; setSDLText(); } } void Text::setObjectText(const std::wstring& text) { if (text != m_wstring) { m_wstring = text; if (m_SDLtext != nullptr) { delete m_SDLtext; m_SDLtext = nullptr; } m_currentCharSize = text.length() + 1; m_SDLtext = new char[m_currentCharSize]; std::wcstombs(m_SDLtext, text.c_str(), text.size()); m_SDLtext[m_currentCharSize - 1] = L'\0'; setSDLText(); } } bool Text::setSDLText() { if (m_string == "" && m_wstring == L"") return false; auto checkFontParam = [this](sf::Font *font, bool normalText) { if (m_characterSize != font->getSize() || m_style != font->m_SDLFontStyle || (!normalText && m_outlineThickness != font->m_SDLoutlineFontSize)) { bool fontExists(false); for (unsigned int _I(0); _I < is::IS_ENGINE_SDL_AUTO_GENERATE_FONT.size(); _I++) { if (is::IS_ENGINE_SDL_AUTO_GENERATE_FONT[_I]->getFileName() == font->getFileName() && is::IS_ENGINE_SDL_AUTO_GENERATE_FONT[_I]->getSize() == m_characterSize && is::IS_ENGINE_SDL_AUTO_GENERATE_FONT[_I]->m_SDLFontStyle == m_style && is::IS_ENGINE_SDL_AUTO_GENERATE_FONT[_I]->m_SDLoutlineFontSize == m_outlineThickness) { if (normalText) m_font = is::IS_ENGINE_SDL_AUTO_GENERATE_FONT[_I]; else m_outlineFont = is::IS_ENGINE_SDL_AUTO_GENERATE_FONT[_I]; fontExists = true; break; } } if (!fontExists) { is::IS_ENGINE_SDL_AUTO_GENERATE_FONT.push_back(new Font(font->getFileName(), m_characterSize)); is::IS_ENGINE_SDL_AUTO_GENERATE_FONT[is::IS_ENGINE_SDL_AUTO_GENERATE_FONT.size() - 1]->m_SDLFontStyle = m_style; TTF_SetFontStyle(is::IS_ENGINE_SDL_AUTO_GENERATE_FONT[is::IS_ENGINE_SDL_AUTO_GENERATE_FONT.size() - 1]->getSDLFont(), m_style); if (!normalText) { is::IS_ENGINE_SDL_AUTO_GENERATE_FONT[is::IS_ENGINE_SDL_AUTO_GENERATE_FONT.size() - 1]->m_SDLoutlineFontSize = m_outlineThickness; TTF_SetFontOutline(is::IS_ENGINE_SDL_AUTO_GENERATE_FONT[is::IS_ENGINE_SDL_AUTO_GENERATE_FONT.size() - 1]->getSDLFont(), m_outlineThickness); m_outlineFont = is::IS_ENGINE_SDL_AUTO_GENERATE_FONT[is::IS_ENGINE_SDL_AUTO_GENERATE_FONT.size() - 1]; } else m_font = is::IS_ENGINE_SDL_AUTO_GENERATE_FONT[is::IS_ENGINE_SDL_AUTO_GENERATE_FONT.size() - 1]; } } }; checkFontParam(m_font, true); if (m_outlineThickness > 0.f) checkFontParam(m_outlineFont, false); if (m_SDLtext == nullptr) return false; m_multiLines = false; short line(0), maxCaracter(0), caracter(0); char currentStr[100]; char finalStr[100]; for (size_t i(0); i < strlen(m_SDLtext); i++) { if (m_SDLtext[i] != '\n') { if (m_SDLtext[i] == '.') currentStr[caracter] = '-'; else if (m_SDLtext[i] == ' ' && m_SDLcontainMultiSpaces) currentStr[caracter] = '_'; else currentStr[caracter] = m_SDLtext[i]; } caracter++; if (m_SDLtext[i] == '\n' || (i == strlen(m_SDLtext) - 1 && m_multiLines)) { currentStr[caracter - 1] = '\0'; if (caracter > maxCaracter) { strcpy(finalStr, currentStr); maxCaracter = caracter; m_multiLines = true; } caracter = 0; line++; } } int w(0), h(0); if (line > 0) { if (TTF_SizeText((m_outlineThickness == 0) ? m_font->getSDLFont() : m_outlineFont->getSDLFont(), finalStr, &w, &h)) {/* allow to show error */} w += ((m_characterSize > 30) ? 6 : 0) + m_SDLaddTextRecWSize; } auto createTexture = [this, &line, &w, &h](SDL_Surface *surface, bool normalText) { surface = NULL; surface = TTF_RenderText_Blended_Wrapped((normalText) ? m_font->getSDLFont() : m_outlineFont->getSDLFont(), m_SDLtext, (normalText) ? getSDLColor(false) : getSDLOutlineColor(), ((line == 0) ? 1280 : w)); if (surface != NULL) { SDL_Texture *texture = NULL; if (normalText) { if (m_SDLtexture != NULL) { SDL_DestroyTexture(m_SDLtexture); m_SDLtexture = NULL; } m_SDLtexture = SDL_CreateTextureFromSurface(is::IS_ENGINE_SDL_renderer, surface); texture = m_SDLtexture; } else { if (m_SDLoutlineTexture != NULL) { SDL_DestroyTexture(m_SDLoutlineTexture); m_SDLoutlineTexture = NULL; } m_SDLoutlineTexture = SDL_CreateTextureFromSurface(is::IS_ENGINE_SDL_renderer, surface); texture = m_SDLoutlineTexture; } if (texture != NULL) { if (normalText) setSize(surface->w, surface->h); { m_SDLoutlineTextureRec.width = surface->w; m_SDLoutlineTextureRec.height = surface->h; } /*if (m_multiLines) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Text: w: %f h: %f \n<%s> line: %d char: %d\n", m_size.x, m_size.y, finalStr, line, maxCaracter); }*/ SDL_FreeSurface(surface); surface = NULL; } else { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Texture Text (\"%s\") : %s\n", m_SDLtext, SDL_GetError()); return false; } } else { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Surface Text (\"%s\") : %s\n", m_SDLtext, TTF_GetError()); return false; } return true; }; bool createdSuccessfully = createTexture(m_SDLsurface, true); if (m_outlineThickness > 0) createdSuccessfully = createTexture(m_SDLsurface, false); return createdSuccessfully; } View::View() { setSize(640.f, 480.f); setCenter(320.f, 240.f); } View::View(const Vector2f& center, const Vector2f& size) { setSize(size.x, size.y); setCenter(center.x, center.y); } void View::setCenter(float x, float y) { is::setVector2(m_center, x, y); } void View::setSize(float width, float height) { is::setVector2(m_size, width, height); } const Vector2f& View::getCenter() const noexcept { return m_center; } void RectangleShape::draw(View const &view) { SDL_Rect rec; SDL_SetRenderDrawColor(is::IS_ENGINE_SDL_renderer, m_color.r, m_color.g, m_color.b, m_color.a); rec.x = ((m_position.x - m_origin.x) - (view.getCenter().x - (view.getSize().x / 2.f))) * is::IS_ENGINE_SDL_screenXScale; rec.y = ((m_position.y - m_origin.y) - (view.getCenter().y - (view.getSize().y / 2.f))) * is::IS_ENGINE_SDL_screenYScale; rec.w = (m_size.x * std::abs(m_scale.x)) * is::IS_ENGINE_SDL_screenXScale; rec.h = (m_size.y * std::abs(m_scale.y)) * is::IS_ENGINE_SDL_screenYScale; SDL_RenderFillRect(is::IS_ENGINE_SDL_renderer, &rec); if (m_outlineThickness > 0.f) { for (int i(0); i < static_cast(m_outlineThickness); i++) { SDL_Rect recOutline; SDL_SetRenderDrawColor(is::IS_ENGINE_SDL_renderer, m_outlineColor.r, m_outlineColor.g, m_outlineColor.b, m_outlineColor.a); recOutline.x = rec.x + i; recOutline.y = rec.y + i; recOutline.w = rec.w - i * 2; recOutline.h = rec.h - i * 2; SDL_RenderDrawRect(is::IS_ENGINE_SDL_renderer, &recOutline); } } } void CircleShape::draw(View const &view) { auto drawCircle = [this, &view](sf::Color const &color, int size) { SDL_SetRenderDrawColor(is::IS_ENGINE_SDL_renderer, color.r, color.g, color.b, color.a); for (int w = 0; w < size * 2; w++) { for (int h = 0; h < size * 2; h++) { int dx = size - w; // horizontal offset int dy = size - h; // vertical offset if ((dx * dx + dy * dy) <= (size * size)) { SDL_RenderDrawPoint(is::IS_ENGINE_SDL_renderer, ((m_position.x - m_origin.x) - (view.getCenter().x - (view.getSize().x / 2.f))) * is::IS_ENGINE_SDL_screenXScale + dx, ((m_position.y - m_origin.y) - (view.getCenter().y - (view.getSize().y / 2.f))) * is::IS_ENGINE_SDL_screenYScale + dy); } } } }; if (m_outlineThickness > 0.f) drawCircle(m_outlineColor, m_size.x); drawCircle(m_color, m_size.x - static_cast(m_outlineThickness)); } bool Keyboard::isKeyPressed(Key key) { const Uint8 *state = SDL_GetKeyboardState(NULL); // These keys have the same ID if (key == Return) key = Enter; if (key == Numpad0) key = Num0; if (key == Numpad1) key = Num1; if (key == Numpad2) key = Num2; if (key == Numpad3) key = Num3; if (key == Numpad4) key = Num4; if (key == Numpad5) key = Num5; if (key == Numpad6) key = Num6; if (key == Numpad7) key = Num7; if (key == Numpad8) key = Num8; if (key == Numpad9) key = Num9; if (key == Numpad9) key = Num9; if (key == BackSpace) key = Backspace; if (key == BackSlash) key = Backslash; if (key == SemiColon) key = Semicolon; switch (key) { case A: if(state[SDL_SCANCODE_A]) return true; break; case B: if(state[SDL_SCANCODE_B]) return true; break; case C: if(state[SDL_SCANCODE_C]) return true; break; case D: if(state[SDL_SCANCODE_D]) return true; break; case E: if(state[SDL_SCANCODE_E]) return true; break; case F: if(state[SDL_SCANCODE_F]) return true; break; case G: if(state[SDL_SCANCODE_G]) return true; break; case H: if(state[SDL_SCANCODE_H]) return true; break; case I: if(state[SDL_SCANCODE_I]) return true; break; case J: if(state[SDL_SCANCODE_J]) return true; break; case K: if(state[SDL_SCANCODE_K]) return true; break; case L: if(state[SDL_SCANCODE_L]) return true; break; case M: if(state[SDL_SCANCODE_M]) return true; break; case N: if(state[SDL_SCANCODE_N]) return true; break; case O: if(state[SDL_SCANCODE_O]) return true; break; case P: if(state[SDL_SCANCODE_P]) return true; break; case Q: if(state[SDL_SCANCODE_Q]) return true; break; case R: if(state[SDL_SCANCODE_R]) return true; break; case S: if(state[SDL_SCANCODE_S]) return true; break; case T: if(state[SDL_SCANCODE_T]) return true; break; case U: if(state[SDL_SCANCODE_U]) return true; break; case V: if(state[SDL_SCANCODE_V]) return true; break; case W: if(state[SDL_SCANCODE_W]) return true; break; case X: if(state[SDL_SCANCODE_X]) return true; break; case Y: if(state[SDL_SCANCODE_Y]) return true; break; case Z: if(state[SDL_SCANCODE_Z]) return true; break; case Num0: if(state[SDL_SCANCODE_0]) return true; break; case Num1: if(state[SDL_SCANCODE_1]) return true; break; case Num2: if(state[SDL_SCANCODE_2]) return true; break; case Num3: if(state[SDL_SCANCODE_3]) return true; break; case Num4: if(state[SDL_SCANCODE_4]) return true; break; case Num5: if(state[SDL_SCANCODE_5]) return true; break; case Num6: if(state[SDL_SCANCODE_6]) return true; break; case Num7: if(state[SDL_SCANCODE_7]) return true; break; case Num8: if(state[SDL_SCANCODE_8]) return true; break; case Num9: if(state[SDL_SCANCODE_9]) return true; break; case Escape: if(state[SDL_SCANCODE_ESCAPE]) return true; break; case LControl: if(state[SDL_SCANCODE_LCTRL]) return true; break; case LShift: if(state[SDL_SCANCODE_LSHIFT]) return true; break; case LAlt: if(state[SDL_SCANCODE_LALT]) return true; break; //case LSystem: if(state[SDL_SCANCODE_LEFT_SUPER]) return true; break; case RControl: if(state[SDL_SCANCODE_RCTRL]) return true; break; case RShift: if(state[SDL_SCANCODE_RSHIFT]) return true; break; case RAlt: if(state[SDL_SCANCODE_RALT]) return true; break; //case RSystem: if(state[SDL_SCANCODE_RIGHT_SUPER]) return true; break; case Menu: if(state[SDL_SCANCODE_MENU]) return true; break; case LBracket: if(state[SDL_SCANCODE_LEFTBRACKET]) return true; break; case RBracket: if(state[SDL_SCANCODE_RIGHTBRACKET]) return true; break; case Semicolon: if(state[SDL_SCANCODE_SEMICOLON]) return true; break; case Comma: if(state[SDL_SCANCODE_COMMA]) return true; break; case Period: if(state[SDL_SCANCODE_PERIOD]) return true; break; case Slash: if(state[SDL_SCANCODE_SLASH]) return true; break; case Backslash: if(state[SDL_SCANCODE_BACKSLASH]) return true; break; case Equal: if(state[SDL_SCANCODE_EQUALS]) return true; break; case Space: if(state[SDL_SCANCODE_SPACE]) return true; break; case Enter: // case Return: same value as Enter if(state[SDL_SCANCODE_RETURN]) return true; break; case Backspace: if(state[SDL_SCANCODE_BACKSPACE]) return true; break; case Tab: if(state[SDL_SCANCODE_TAB]) return true; break; case PageUp: if(state[SDL_SCANCODE_PAGEUP]) return true; break; case PageDown: if(state[SDL_SCANCODE_PAGEDOWN]) return true; break; case End: if(state[SDL_SCANCODE_END]) return true; break; case Home: if(state[SDL_SCANCODE_HOME]) return true; break; case Insert: if(state[SDL_SCANCODE_INSERT]) return true; break; case Delete: if(state[SDL_SCANCODE_DELETE]) return true; break; //case Add: if(state[SDL_SCANCODE_KP_ADD]) return true; break; //case Subtract: if(state[SDL_SCANCODE_KP_SUBTRACT]) return true; break; case Multiply: if(state[SDL_SCANCODE_KP_MULTIPLY]) return true; break; case Divide: if(state[SDL_SCANCODE_KP_DIVIDE]) return true; break; case Left: if(state[SDL_SCANCODE_LEFT]) return true; break; case Right: if(state[SDL_SCANCODE_RIGHT]) return true; break; case Up: if(state[SDL_SCANCODE_UP]) return true; break; case Down: if(state[SDL_SCANCODE_DOWN]) return true; break; /* case Numpad0: if(state[SDL_SCANCODE_0]) return true; break; case Numpad1: if(state[SDL_SCANCODE_1]) return true; break; case Numpad2: if(state[SDL_SCANCODE_2]) return true; break; case Numpad3: if(state[SDL_SCANCODE_3]) return true; break; case Numpad4: if(state[SDL_SCANCODE_4]) return true; break; case Numpad5: if(state[SDL_SCANCODE_5]) return true; break; case Numpad6: if(state[SDL_SCANCODE_6]) return true; break; case Numpad7: if(state[SDL_SCANCODE_7]) return true; break; case Numpad8: if(state[SDL_SCANCODE_8]) return true; break; case Numpad9: if(state[SDL_SCANCODE_9]) return true; break; */ case F1: if(state[SDL_SCANCODE_F1]) return true; break; case F2: if(state[SDL_SCANCODE_F2]) return true; break; case F3: if(state[SDL_SCANCODE_F3]) return true; break; case F4: if(state[SDL_SCANCODE_F4]) return true; break; case F5: if(state[SDL_SCANCODE_F5]) return true; break; case F6: if(state[SDL_SCANCODE_F6]) return true; break; case F7: if(state[SDL_SCANCODE_F7]) return true; break; case F8: if(state[SDL_SCANCODE_F8]) return true; break; case F9: if(state[SDL_SCANCODE_F9]) return true; break; case F10: if(state[SDL_SCANCODE_F10]) return true; break; case F11: if(state[SDL_SCANCODE_F11]) return true; break; case F12: if(state[SDL_SCANCODE_F12]) return true; break; case F13: if(state[SDL_SCANCODE_F13]) return true; break; case F14: if(state[SDL_SCANCODE_F14]) return true; break; case F15: if(state[SDL_SCANCODE_F15]) return true; break; case Pause: if(state[SDL_SCANCODE_PAUSE]) return true; break; /* case BackSpace: if(state[SDL_SCANCODE_BACKSPACE]) return true; break; case BackSlash: if(state[SDL_SCANCODE_BACKSLASH]) return true; break; case SemiColon: if(state[SDL_SCANCODE_SEMICOLON]) return true; break; */ default: return false; break; } return false; } VideoMode VideoMode::getDesktopMode() { VideoMode videoMode; videoMode.width = is::IS_ENGINE_SDL_displayMode.w; videoMode.height = is::IS_ENGINE_SDL_displayMode.h; return videoMode; } RenderWindow::~RenderWindow() { if (m_SDLiconSurface != NULL) { SDL_FreeSurface(m_SDLiconSurface); m_SDLiconSurface = NULL; } } void RenderWindow::create(VideoMode videoMode, const std::string& title, int style) { is::setVector2(m_size, videoMode.width, videoMode.height); m_view.setSize(videoMode.width, videoMode.height); m_title = title; m_style = style; int w, h; #if (defined(__ANDROID__)) w = is::IS_ENGINE_SDL_displayMode.w; h = is::IS_ENGINE_SDL_displayMode.h; #else SDL_GetWindowSize(is::IS_ENGINE_SDL_window, &w, &h); #endif if (SDL_Init(SDL_INIT_VIDEO #if defined(__SWITCH__) | SDL_INIT_GAMECONTROLLER #endif ) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "init SDL Video : %s\n", SDL_GetError()); is::closeApplication(); } // Allows to calculate the scale of the screen is::IS_ENGINE_SDL_screenXScale = w / m_view.getSize().x; is::IS_ENGINE_SDL_screenYScale = h / m_view.getSize().y; is::IS_ENGINE_SDL_window = SDL_CreateWindow(m_title.c_str(), #if !defined(__ANDROID__) SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, m_size.x, m_size.y, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL #else SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, m_size.x, m_size.y, SDL_WINDOW_OPENGL #endif ); is::IS_ENGINE_SDL_renderer = SDL_CreateRenderer(is::IS_ENGINE_SDL_window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); #if defined(__SWITCH__) if (SDL_NumJoysticks() > 0 && SDL_IsGameController(0)) { is::IS_ENGINE_SDL_controller = SDL_GameControllerOpen(0); } #endif if (!is::SDL2initLib()) is::closeApplication(); } void RenderWindow::setFramerateLimit(int fps) { // m_windowFrameLimit = 1000 / fps; m_windowFrameLimit = (fps * 50) / 60; } void RenderWindow::setSize(const Vector2u& size) { is::setVector2(m_size, size.x, size.y); SDL_SetWindowSize(is::IS_ENGINE_SDL_window, size.x, size.y); } void RenderWindow::setTitle(const std::string& text) { m_title = text; SDL_SetWindowTitle(is::IS_ENGINE_SDL_window, m_title.c_str()); } void RenderWindow::setView(const View& view) { m_view = view; // When we change the size of the view we recalculate the scale of the screen int w, h; #if (defined(__ANDROID__)) w = is::IS_ENGINE_SDL_displayMode.w; h = is::IS_ENGINE_SDL_displayMode.h; #else SDL_GetWindowSize(is::IS_ENGINE_SDL_window, &w, &h); #endif is::IS_ENGINE_SDL_screenXScale = w / m_view.getSize().x; is::IS_ENGINE_SDL_screenYScale = h / m_view.getSize().y; } void RenderWindow::setPosition(Vector2i position) { SDL_SetWindowPosition(is::IS_ENGINE_SDL_window, position.x, position.y); } void RenderWindow::setPosition(int x, int y) { SDL_SetWindowPosition(is::IS_ENGINE_SDL_window, x, y); } void RenderWindow::setVerticalSyncEnabled(bool vsync) { SDL_GL_SetSwapInterval(vsync); } void RenderWindow::setIcon(Uint32 width, Uint32 height, const Uint8* pixels) { // This part is automatically managed when we define the icon in the "resource.rc" file } void RenderWindow::clear(sf::Color const &color) { SDL_SetRenderDrawColor(is::IS_ENGINE_SDL_renderer, color.r, color.g, color.b, color.a); SDL_RenderClear(is::IS_ENGINE_SDL_renderer); } void RenderWindow::draw(SDLTexture &obj) { if (obj.getSDLTexture() == NULL) return; SDL_Rect rec; SDL_Rect recSrc; SDL_Point point; // We keep the value of the origin x and y of the object to be drawn. // This allows to keep the value of the original variables intact during recalculations. float objOriginX((obj.getScale().x < 0.f /*&& static_cast(obj.getOrigin().y) == 0*/ && static_cast(obj.getOrigin().x) == 0) ? obj.getTextureRect().width : obj.getOrigin().x); float objOriginY((obj.getScale().y < 0.f /*&& static_cast(obj.getOrigin().x) == 0*/ && static_cast(obj.getOrigin().y) == 0) ? obj.getTextureRect().height : obj.getOrigin().y); float xOrigin(objOriginX); float yOrigin(objOriginY); // Image angle float rotation(obj.getRotation()); // Use to stretch the image of the object to make a scale effect rec.w = obj.getTextureRect().width * std::abs(obj.getScale().x); rec.h = obj.getTextureRect().height * std::abs(obj.getScale().y); // Each time the image is scale, the origin of the object is recalculated if (std::abs(obj.getScale().x) > 1.001f || std::abs(obj.getScale().x) < 0.999f) xOrigin = rec.w / (obj.getTextureRect().width / objOriginX); if (std::abs(obj.getScale().y) > 1.001f || std::abs(obj.getScale().y) < 0.999f) yOrigin = rec.h / (obj.getTextureRect().height / objOriginY); // We do a corresponding flip according to the sign of the variables x scale and y scale if (obj.getScale().x < 0.f && obj.getScale().y < 0.f) { obj.m_SDLFlip = (SDL_RendererFlip)(SDL_FLIP_HORIZONTAL | SDL_FLIP_VERTICAL); if (static_cast(obj.getOrigin().x) != static_cast(obj.getTextureRect().width / 2) && static_cast(obj.getOrigin().x) != 0) xOrigin += obj.getTextureRect().width; if (static_cast(obj.getOrigin().y) != static_cast(obj.getTextureRect().height / 2) && static_cast(obj.getOrigin().y) != 0) yOrigin -= obj.getTextureRect().height; } else if (obj.getScale().x < 0.f) { obj.m_SDLFlip = SDL_FLIP_HORIZONTAL; if (static_cast(obj.getOrigin().x) != static_cast(obj.getTextureRect().width / 2) && static_cast(obj.getOrigin().x) != 0) xOrigin += obj.getTextureRect().width; } else if (obj.getScale().y < 0.f) { obj.m_SDLFlip = SDL_FLIP_VERTICAL; if (static_cast(obj.getOrigin().y) != static_cast(obj.getTextureRect().height / 2) && static_cast(obj.getOrigin().y) != 0) yOrigin -= obj.getTextureRect().height; } else obj.m_SDLFlip = SDL_FLIP_NONE; // Move the object according to the position of the camera rec.x = (obj.getPosition().x - xOrigin) - (m_view.getCenter().x - (m_view.getSize().x / 2.f)); rec.y = (obj.getPosition().y - yOrigin) - (m_view.getCenter().y - (m_view.getSize().y / 2.f)); // We apply the scale of the screen to the object in order to resize it in relation to the screen rec.x *= is::IS_ENGINE_SDL_screenXScale; rec.y *= is::IS_ENGINE_SDL_screenYScale; rec.w *= is::IS_ENGINE_SDL_screenXScale; rec.h *= is::IS_ENGINE_SDL_screenYScale; recSrc.x = obj.getTextureRect().left; recSrc.y = obj.getTextureRect().top; recSrc.w = obj.getTextureRect().width; recSrc.h = obj.getTextureRect().height; point.x = xOrigin * is::IS_ENGINE_SDL_screenXScale; point.y = yOrigin * is::IS_ENGINE_SDL_screenYScale; if (obj.getTextureRect().width == obj.getTextureRect().height && rec.w != rec.h && obj.m_circleShape && std::abs(obj.getRotation()) > 0 && std::abs(obj.getScale().x) == std::abs(obj.getScale().y)) { auto redimImg = [this](int &destSize, int &destPos, int &destOrigin, float &origin, const int &srcSize, const int &size, const float &objOrigin) { destPos += (destSize - srcSize) / 2; destSize = srcSize; if (std::abs(objOrigin) > 0.f) origin = destSize / (size / objOrigin); destOrigin = origin; }; if (rec.w > rec.h) redimImg(rec.w, rec.x, point.x, xOrigin, rec.h, obj.getTextureRect().width, objOriginX); else redimImg(rec.h, rec.y, point.y, yOrigin, rec.w, obj.getTextureRect().height, objOriginY); } if (obj.m_SDLTextureType == SDLTexture::SDLTextureType::IS_ENGINE_SDL_TEXT) { if (obj.getSDLOutlineTexture() != NULL) { SDL_SetTextureBlendMode(obj.getSDLOutlineTexture(), SDL_BLENDMODE_BLEND); SDL_SetTextureAlphaMod(obj.getSDLOutlineTexture(), obj.getColor().a); SDL_Rect SDLoutlineTextureRec, outlineTextureRecSrc; SDLoutlineTextureRec.x = rec.x; SDLoutlineTextureRec.y = rec.y; SDLoutlineTextureRec.w = obj.m_SDLoutlineTextureRec.width * std::abs(obj.getScale().x); SDLoutlineTextureRec.h = obj.m_SDLoutlineTextureRec.height * std::abs(obj.getScale().y); SDLoutlineTextureRec.w *= is::IS_ENGINE_SDL_screenXScale; SDLoutlineTextureRec.h *= is::IS_ENGINE_SDL_screenYScale; outlineTextureRecSrc.x = obj.getTextureRect().left; outlineTextureRecSrc.y = obj.getTextureRect().top; outlineTextureRecSrc.w = obj.m_SDLoutlineTextureRec.width; outlineTextureRecSrc.h = obj.m_SDLoutlineTextureRec.height; SDL_RenderCopyEx(is::IS_ENGINE_SDL_renderer, obj.getSDLOutlineTexture(), &outlineTextureRecSrc, &SDLoutlineTextureRec, rotation, &point, obj.m_SDLFlip); } } SDL_SetTextureBlendMode(obj.getSDLTexture(), SDL_BLENDMODE_BLEND); SDL_SetTextureAlphaMod(obj.getSDLTexture(), obj.getColor().a); SDL_SetTextureColorMod(obj.getSDLTexture(), obj.getColor().r, obj.getColor().g, obj.getColor().b); SDL_RenderCopyEx(is::IS_ENGINE_SDL_renderer, obj.getSDLTexture(), &recSrc, &rec, rotation, &point, obj.m_SDLFlip); } void RenderWindow::display() { SDL_RenderPresent(is::IS_ENGINE_SDL_renderer); SDL_UpdateWindowSurface(is::IS_ENGINE_SDL_window); if (m_windowFrameLimit != 0) { Uint64 diff = std::chrono::duration_cast( std::chrono::steady_clock::now() - m_timeSinceLastDisplay).count(); if (diff < 1000 / m_windowFrameLimit) { SDL_Delay(static_cast(1000 / m_windowFrameLimit - diff)); } } m_timeSinceLastDisplay = std::chrono::steady_clock::now(); } bool RenderWindow::pollEvent(Event &event) { int pollEvenValue = SDL_PollEvent(&event.m_event); event.type = event.m_event.type; event.key.code = event.m_event.key.keysym.sym; if (event.type == sf::Event::MouseButtonPressed || event.type == sf::Event::MouseButtonReleased) { switch(event.m_event.button.button) { case SDL_BUTTON_LEFT: event.key.code = sf::Mouse::Left; break; case SDL_BUTTON_RIGHT: event.key.code = sf::Mouse::Right; break; case SDL_BUTTON_MIDDLE: event.key.code = sf::Mouse::Middle; break; case SDL_BUTTON_X1: event.key.code = sf::Mouse::XButton1; break; case SDL_BUTTON_X2: event.key.code = sf::Mouse::XButton2; break; } } if (event.type == sf::Event::MouseWheelMoved) { if (event.m_event.wheel.y > 0) // scroll up { event.mouseWheel.delta++; } else if (event.m_event.wheel.y < 0) // scroll down { event.mouseWheel.delta--; } } #ifdef __SWITCH__ is::updateButtonState(); #endif if (is::IS_ENGINE_MOBILE_OS) { if (pollEvenValue == 1) { m_tempScreenXScale = is::IS_ENGINE_SDL_screenXScale; m_tempScreenYScale = is::IS_ENGINE_SDL_screenYScale; #if (defined(IS_ENGINE_HTML_5) || defined(__SWITCH__)) if (is::IS_ENGINE_MOBILE_OS) { m_tempScreenXScale = is::IS_ENGINE_SDL_displayMode.w / m_view.getSize().x; m_tempScreenYScale = is::IS_ENGINE_SDL_displayMode.h / m_view.getSize().y; } #endif switch (event.m_event.type) { case SDL_FINGERDOWN: { if (is::IS_ENGINE_SDL_touchIdCount < is::IS_ENGINE_SDL_TOUCH_ID_COUNT_MAX) { int tempTouchId = is::IS_ENGINE_SDL_touchIdCount; if (is::IS_ENGINE_SDL_touchData[1].m_SDLtouchDown && !is::IS_ENGINE_SDL_touchData[0].m_SDLtouchDown) tempTouchId = 0; is::IS_ENGINE_SDL_touchData[tempTouchId].m_SDLtouchX = (event.m_event.tfinger.x * is::IS_ENGINE_SDL_displayMode.w) / m_tempScreenXScale; is::IS_ENGINE_SDL_touchData[tempTouchId].m_SDLtouchY = (event.m_event.tfinger.y * is::IS_ENGINE_SDL_displayMode.h) / m_tempScreenYScale; is::IS_ENGINE_SDL_touchData[tempTouchId].m_SDLtouchDown = true; is::IS_ENGINE_SDL_touchIdCount++; if (is::IS_ENGINE_SDL_touchIdCount == 2) is::IS_ENGINE_SDL_touchIdLast = tempTouchId; } } break; case SDL_FINGERMOTION: { if (is::IS_ENGINE_SDL_enableFINGERMOTION) { if (is::IS_ENGINE_SDL_touchIdCount == 1) { int tempTouchId = 0; if (is::IS_ENGINE_SDL_touchData[1].m_SDLtouchDown && !is::IS_ENGINE_SDL_touchData[0].m_SDLtouchDown) tempTouchId = 1; is::IS_ENGINE_SDL_touchData[tempTouchId].m_SDLtouchX = (event.m_event.tfinger.x * is::IS_ENGINE_SDL_displayMode.w) / m_tempScreenXScale; is::IS_ENGINE_SDL_touchData[tempTouchId].m_SDLtouchY = (event.m_event.tfinger.y * is::IS_ENGINE_SDL_displayMode.h) / m_tempScreenYScale; } if (is::IS_ENGINE_SDL_touchIdCount == 2) { is::IS_ENGINE_SDL_touchData[is::IS_ENGINE_SDL_touchIdLast].m_SDLtouchX = (event.m_event.tfinger.x * is::IS_ENGINE_SDL_displayMode.w) / m_tempScreenXScale; is::IS_ENGINE_SDL_touchData[is::IS_ENGINE_SDL_touchIdLast].m_SDLtouchY = (event.m_event.tfinger.y * is::IS_ENGINE_SDL_displayMode.h) / m_tempScreenYScale; } } } break; case SDL_FINGERUP: { if (is::IS_ENGINE_SDL_touchIdCount > 0) { int releaseTouchX = (event.m_event.tfinger.x * is::IS_ENGINE_SDL_displayMode.w) / m_tempScreenXScale; int releaseTouchY = (event.m_event.tfinger.y * is::IS_ENGINE_SDL_displayMode.h) / m_tempScreenYScale; int nearTouchId = 0; if (is::IS_ENGINE_SDL_touchIdCount == 2) { float disTouch0 = is::pointDistance(releaseTouchX, releaseTouchY, is::IS_ENGINE_SDL_touchData[0].m_SDLtouchX, is::IS_ENGINE_SDL_touchData[0].m_SDLtouchY); float disTouch1 = is::pointDistance(releaseTouchX, releaseTouchY, is::IS_ENGINE_SDL_touchData[1].m_SDLtouchX, is::IS_ENGINE_SDL_touchData[1].m_SDLtouchY); nearTouchId = ((disTouch0 > disTouch1) ? 1 : 0); } else if (is::IS_ENGINE_SDL_touchData[1].m_SDLtouchDown) nearTouchId = 1; is::IS_ENGINE_SDL_touchData[nearTouchId].m_SDLtouchX = 0; is::IS_ENGINE_SDL_touchData[nearTouchId].m_SDLtouchY = 0; is::IS_ENGINE_SDL_touchData[nearTouchId].m_SDLtouchDown = false; is::IS_ENGINE_SDL_touchIdCount--; } } break; case SDL_WINDOWEVENT_RESIZED: event.size.width = event.m_event.window.data1; event.size.height = event.m_event.window.data2; break; } } } if (event.type == 0 || event.type == 512 || event.type == 1024) event.type = ((SDL_GetWindowFlags(is::IS_ENGINE_SDL_window) & SDL_WINDOW_INPUT_FOCUS) ? -1 : -2); return ((pollEvenValue == 1) ? true : false); } bool RenderWindow::waitEvent(Event &event) { while (!pollEvent(event)) continue; return true; } Vector2i RenderWindow::getPosition() const { int x = 0, y = 0; SDL_GetWindowPosition(is::IS_ENGINE_SDL_window, &x, &y); return Vector2i(x, y); } Vector2f RenderWindow::mapPixelToCoords(const Vector2i& point, const View& view) const { Vector2f pos{static_cast((point.x #if (!defined(__ANDROID__)) / is::IS_ENGINE_SDL_screenXScale #endif ) + (view.getCenter().x - (view.getSize().x / 2.f))), static_cast((point.y #if (!defined(__ANDROID__)) / is::IS_ENGINE_SDL_screenYScale #endif ) + (view.getCenter().y - (view.getSize().y / 2.f)))}; return pos; } void SoundBuffer::setChannelId() { m_channel = SDL_sndChannel++; for (int i(0); i < is::IS_ENGINE_SDL_CHANNEL_MAX; i++) { if (is::IS_ENGINE_SDL_channel[i] == m_channel) { m_channel = 0; break; } } for (int i(0); i < is::IS_ENGINE_SDL_CHANNEL_MAX; i++) { if (is::IS_ENGINE_SDL_channel[i] == m_channel) m_channel++; else break; } is::IS_ENGINE_SDL_channel[m_channel] = m_channel; } SoundBuffer::SoundBuffer() { setChannelId(); } SoundBuffer::SoundBuffer(const std::string filename): m_filename(filename) { setChannelId(); loadSound(m_filename); } SoundBuffer::~SoundBuffer() { SDL_sndChannel--; is::IS_ENGINE_SDL_channel[m_channel] = -1; Mix_FreeChunk(m_SDLsound); m_SDLsound = NULL; } bool SoundBuffer::loadSound(const std::string& filePath) { m_SDLsound = Mix_LoadWAV(filePath.c_str()); if (m_SDLsound == NULL) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Can't load SoundBuffer : %s \"%s\"\n", Mix_GetError(), filePath.c_str()); return false; } return true; } Sound::Status Sound::getStatus() { if (m_status != Status::Paused) { if (Mix_Playing(m_SDLsoundBuffer->getSDLChannel()) == 0) m_status = Stopped; } return m_status; } void Sound::play() { if (m_status != Status::Paused) { Mix_PlayChannel(m_SDLsoundBuffer->getSDLChannel(), m_SDLsoundBuffer->getSDLChunk(), ((m_loop) ? -1 : 0)); } else { Mix_Resume(m_SDLsoundBuffer->getSDLChannel()); } m_status = Status::Playing; } void Sound::setPitch(float speed) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "This function is not yet implemented!"); } void Sound::setVolume(float volume) { if (static_cast(volume) >= 0 && static_cast(volume) <= 100) Mix_VolumeChunk(m_SDLsoundBuffer->getSDLChunk(), volume * MIX_MAX_VOLUME / 100); } Music::Music() : #if !defined(__ANDROID__) SoundSource() { Mix_VolumeMusic(MIX_MAX_VOLUME); #else Sound() { #endif } Music::~Music() { #if defined(__ANDROID__) if (m_SDLsoundBuffer != nullptr) { delete m_SDLsoundBuffer; m_SDLsoundBuffer = nullptr; } #else Mix_FreeMusic(m_music); m_music = NULL; #endif } Music::Status Music::getStatus() { if (m_status != Status::Paused) { #if !defined(__ANDROID__) if (Mix_PlayingMusic() == 0) m_status = Stopped; #else if (Mix_Playing(m_SDLsoundBuffer->getSDLChannel()) == 0) m_status = Stopped; #endif } return m_status; } #if !defined(__ANDROID__) void Music::play() { if (m_status != Status::Paused) { Mix_PlayMusic(m_music, ((m_loop) ? -1 : 0)); } else { Mix_ResumeMusic(); } m_status = Status::Playing; } void Music::setPitch(float speed) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "This function is not yet implemented!"); } void Music::setVolume(float volume) { if (static_cast(volume) >= 0 && static_cast(volume) <= 100) Mix_VolumeMusic(volume * MIX_MAX_VOLUME / 100); } #endif bool Music::openFromFile(const std::string& filePath) { #if defined(__ANDROID__) if (m_SDLsoundBuffer != nullptr) { delete m_SDLsoundBuffer; m_SDLsoundBuffer = nullptr; } m_SDLsoundBuffer = new SoundBuffer(); return m_SDLsoundBuffer->loadFromFile(filePath.c_str()); #else m_music = Mix_LoadMUS(filePath.c_str()); if (m_music == NULL) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Can't load Music : %s \"%s\"\n", Mix_GetError(), filePath.c_str()); return false; } return true; #endif } bool Mouse::isButtonPressed(Button button) { return (getSDLButtonState() & SDL_BUTTON(button)); } Vector2i Mouse::getPosition() { int x = 0, y = 0; #if !defined(IS_ENGINE_HTML_5) SDL_GetGlobalMouseState #else SDL_GetMouseState #endif (&x, &y); return Vector2i(x, y); } Vector2i Mouse::getPosition(const RenderWindow& relativeTo) { return getPosition() #if !defined(IS_ENGINE_HTML_5) - relativeTo.getPosition() #endif ; } void Mouse::setPosition(const Vector2i& position) { SDL_WarpMouseGlobal(position.x, position.y); } void Mouse::setPosition(const Vector2i& position, const RenderWindow& relativeTo) { SDL_WarpMouseInWindow(is::IS_ENGINE_SDL_window, position.x, position.y); } Uint32 Mouse::getSDLButtonState() { return SDL_GetMouseState(NULL, NULL); } bool Touch::isDown(unsigned int finger) { if (finger <= is::IS_ENGINE_SDL_TOUCH_ID_COUNT_MAX) { return is::IS_ENGINE_SDL_touchData[finger].m_SDLtouchDown; } return false; } Vector2i Touch::getPosition(unsigned int finger) { Vector2i pos{0, 0}; if (finger <= is::IS_ENGINE_SDL_TOUCH_ID_COUNT_MAX) { pos.x = is::IS_ENGINE_SDL_touchData[finger].m_SDLtouchX; pos.y = is::IS_ENGINE_SDL_touchData[finger].m_SDLtouchY; } return pos; } Vector2i Touch::getPosition(unsigned int finger, const RenderWindow& relativeTo) { return getPosition(finger); } } #endif ================================================ FILE: app/src/main/cpp/isEngineWrapper.cpp ================================================ /* is::Engine (Infinity Solutions Engine) Copyright (C) 2018-2024 Is Daouda This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "isEngine/system/islibconnect/isEngineWrapper.h" #if defined(IS_ENGINE_SDL_2) namespace sf { bool Rect::intersects(Rect const &rec) const { int x1Other = rec.left; int x2Other = rec.left + rec.width; int y1Other = rec.top; int y2Other = rec.top + rec.height; int x1 = left; int x2 = left + width; int y1 = top; int y2 = top + height; if (y2 <= y1Other) return false; if (y1 >= y2Other) return false; if (x2 <= x1Other) return false; if (x1 >= x2Other) return false; return true; } bool Rect::intersects(Rect const &rec1, Rect const &rec2) const { return rec1.intersects(rec2); } Rect functionGetGlobalBounds(const Vector2f &position, const Vector2f &origin, const Vector2f &size) { Rect aabb; aabb.left = position.x - origin.x; aabb.top = position.y - origin.y; aabb.width = size.x; aabb.height = size.y; return aabb; } Clock::Clock() : m_startTime(seconds(0.018f)) {} const Time Clock::getElapsedTime() { return m_startTime; } Time Clock::restart() { return m_startTime; } //////////////////////////////////////////////////////////// // Time Operator and Function //////////////////////////////////////////////////////////// const Time Time::Zero; // functions Time seconds(float amount) { // Special case: allows the SMK timer to have the same behavior as that of the SFML Pong example if (amount > 0.09f && amount < 0.101f) { sf::Clock dTime; amount = dTime.getElapsedTime().asSeconds() - 0.01f; } return Time(static_cast(amount * 1000000)); } Time milliseconds(Int32 amount) {return Time(static_cast(amount) * 1000);} Time microseconds(Int64 amount) {return Time(amount);} // operator inline bool operator ==(Time left, Time right) {return left.asMicroseconds() == right.asMicroseconds();} inline bool operator !=(Time left, Time right) {return left.asMicroseconds() != right.asMicroseconds();} inline bool operator <(Time left, Time right) {return left.asMicroseconds() < right.asMicroseconds();} inline bool operator >(Time left, Time right) {return left.asMicroseconds() > right.asMicroseconds();} inline bool operator <=(Time left, Time right){return left.asMicroseconds() <= right.asMicroseconds();} inline bool operator >=(Time left, Time right){return left.asMicroseconds() >= right.asMicroseconds();} inline Time operator -(Time right) {return microseconds(-right.asMicroseconds());} inline Time operator +(Time left, Time right) {return microseconds(left.asMicroseconds() + right.asMicroseconds());} inline Time& operator +=(Time& left, Time right) {return left = left + right;} inline Time operator -(Time left, Time right) {return microseconds(left.asMicroseconds() - right.asMicroseconds());} inline Time& operator -=(Time& left, Time right) {return left = left - right;} inline Time operator *(Time left, float right) {return seconds(left.asSeconds() * right);} inline Time operator *(Time left, Int64 right) {return microseconds(left.asMicroseconds() * right);} inline Time operator *(float left, Time right) {return right * left;} inline Time operator *(Int64 left, Time right) {return right * left;} inline Time& operator *=(Time& left, float right) {return left = left * right;} inline Time& operator *=(Time& left, Int64 right) {return left = left * right;} inline Time operator /(Time left, float right) {return seconds(left.asSeconds() / right);} inline Time operator /(Time left, Int64 right) {return microseconds(left.asMicroseconds() / right);} inline Time& operator /=(Time& left, float right) {return left = left / right;} inline Time& operator /=(Time& left, Int64 right) {return left = left / right;} inline float operator /(Time left, Time right) {return left.asSeconds() / right.asSeconds();} inline Time operator %(Time left, Time right) {return microseconds(left.asMicroseconds() % right.asMicroseconds());} inline Time& operator %=(Time& left, Time right) {return left = left % right;} } #endif ================================================ FILE: app/src/main/cpp/main.cpp ================================================ /* is::Engine (Infinity Solution Engine) Copyright (C) 2018-2025 Is Daouda 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. */ #if defined(IS_ENGINE_VS_CODE) #include "Main.hpp" #endif #include "isEngine/core/GameEngine.h" //////////////////////////////////////////////////////////// /// \brief application entry point //////////////////////////////////////////////////////////// int main(int argc, char * argv[]) { #if defined(IS_ENGINE_HTML_5) EM_ASM( FS.mkdir("save"); FS.mount(IDBFS, {}, "save"); Module.print("Start file sync..."); Module.syncdone = 0; FS.syncfs(true, function(err){ Module.print("End file sync.."); Module.syncdone = 1; }); ); #elif defined(IS_ENGINE_SWITCH) romfsInit(); chdir("romfs:/"); #endif #if !defined(IS_ENGINE_HTML_5) is::GameEngine game; #else is::GameEngine *game = new is::GameEngine(); #endif #if defined(IS_ENGINE_VS_CODE) #if defined(_DEBUG) // Display a text in the console to inform that we are in Debug mode on Visual Studio Code is::showLog("Debug Mode Start!"); #endif #ifdef SFML_SYSTEM_WINDOWS // Allows to create the icon for the application when developing with Visual Studio Code windowsHelper.setIcon(game.getRenderWindow().getSystemHandle()); #endif #endif #if !defined(IS_ENGINE_HTML_5) game. #else game-> #endif #if defined(IS_ENGINE_USE_MAIN_LOOP) play #else basicSFMLmain #endif (); #if defined(IS_ENGINE_HTML_5) delete game; game = nullptr; #endif #if defined(IS_ENGINE_SDL_2) is::SDL2freeLib(); #endif #if defined (__ANDROID__) std::terminate(); // close application #else #ifdef __SWITCH__ romfsExit(); #endif return 0; #endif } ================================================ FILE: app/src/main/cpp/resource.h ================================================ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by SDL2 Template.rc // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 101 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif ================================================ FILE: app/src/main/cpp/vscode/Linux/LinuxHelper.cpp ================================================ #ifdef __linux__ #include "LinuxHelper.hpp" #include /****************************************************************************** * *****************************************************************************/ LinuxHelper::LinuxHelper() { XInitThreads(); } #endif ================================================ FILE: app/src/main/cpp/vscode/Linux/LinuxHelper.hpp ================================================ #ifndef LINUX_HELPER_HPP #define LINUX_HELPER_HPP class LinuxHelper { public: LinuxHelper(); }; #endif // LINUX_HELPER_HPP ================================================ FILE: app/src/main/cpp/vscode/MacOS/MacHelper.cpp ================================================ #ifdef __APPLE__ #include "MacHelper.hpp" #include "CoreFoundation/CoreFoundation.h" /****************************************************************************** * *****************************************************************************/ MacHelper::MacHelper() { // This function ensures the working directory is set inside of the bundle if in production mode CFBundleRef mainBundle = CFBundleGetMainBundle(); CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle); char path[PATH_MAX]; bool pathSet = CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8*)path, PATH_MAX); // This is a copy, so we release it here CFRelease(resourcesURL); // Actually do the check here if (pathSet) { std::string pathStr = path; if (pathStr.find(".app") != std::string::npos) chdir(path); } } #endif ================================================ FILE: app/src/main/cpp/vscode/MacOS/MacHelper.hpp ================================================ #ifndef MACOS_HELPER_HPP #define MACOS_HELPER_HPP class MacHelper { public: MacHelper(); }; #endif // MACOS_HELPER_HPP ================================================ FILE: app/src/main/cpp/vscode/Utility/Types.hpp ================================================ #ifndef UTIL_TYPES_HPP #define UTIL_TYPES_HPP #include typedef std::uint8_t uchar; typedef std::uint16_t ushort; typedef std::uint32_t uint; typedef std::uint64_t ullong; typedef std::int64_t llong; #endif // UTIL_TYPES_HPP ================================================ FILE: app/src/main/cpp/vscode/Win32/Icon.h ================================================ #ifndef ICON_H #define ICON_H #define WIN32_ICON_MAIN 1 #endif // ICON_H ================================================ FILE: app/src/main/cpp/vscode/Win32/Icon.rc ================================================ #ifdef _WIN32 #include "Icon.h" WIN32_ICON_MAIN ICON "env/windows/icon.ico" #endif ================================================ FILE: app/src/main/cpp/vscode/Win32/WindowsHelper.cpp ================================================ #ifdef _WIN32 #include "WindowsHelper.hpp" #include "Icon.h" /****************************************************************************** * *****************************************************************************/ WindowsHelper::WindowsHelper() { // Get the icon directory PBYTE iconDirectory = getIconDirectory(WIN32_ICON_MAIN); // Store each icon m_hIcon32 = getIconFromIconDirectory(iconDirectory, 32); m_hIcon16 = getIconFromIconDirectory(iconDirectory, 16); } /****************************************************************************** * The window handle uses 32x32 (ICON_BIG) & 16x16 (ICON_SMALL) sized icons. * This should be called any time the SFML window is create/recreated *****************************************************************************/ void WindowsHelper::setIcon(const HWND& inHandle) { if (m_hIcon32) SendMessage(inHandle, WM_SETICON, ICON_BIG, (LPARAM)m_hIcon32); if (m_hIcon16) SendMessage(inHandle, WM_SETICON, ICON_SMALL, (LPARAM)m_hIcon16); } /****************************************************************************** * Loads a .ico file from The application's resources, and can contain multiple * sizes (for instance 16x16, 32x32 & 64x64). This is referred to as an * "Icon Directory". Additionally, it can have a single icon *****************************************************************************/ PBYTE WindowsHelper::getIconDirectory(const int& inResourceId) { HMODULE hModule = GetModuleHandle(nullptr); HRSRC hResource = FindResource(hModule, MAKEINTRESOURCE(inResourceId), RT_GROUP_ICON); HGLOBAL hData = LoadResource(hModule, hResource); PBYTE data = (PBYTE)LockResource(hData); return data; } /****************************************************************************** * This will attempt to load a single icon from an icon directory * If the requested size isn't found, the first one is returned *****************************************************************************/ HICON WindowsHelper::getIconFromIconDirectory(const PBYTE& inIconDirectory, const uint& inSize) { HMODULE hModule = GetModuleHandle(nullptr); int resourceId = LookupIconIdFromDirectoryEx(inIconDirectory, TRUE, inSize, inSize, LR_DEFAULTCOLOR); HRSRC hResource = FindResource(hModule, MAKEINTRESOURCE(resourceId), RT_ICON); HGLOBAL hData = LoadResource(hModule, hResource); PBYTE data = (PBYTE)LockResource(hData); DWORD sizeofData = SizeofResource(hModule, hResource); HICON icon = CreateIconFromResourceEx(data, sizeofData, TRUE, 0x00030000, inSize, inSize, LR_DEFAULTCOLOR); return icon; } #endif ================================================ FILE: app/src/main/cpp/vscode/Win32/WindowsHelper.hpp ================================================ #ifndef WINDOWS_HELPER_HPP #define WINDOWS_HELPER_HPP class WindowsHelper { public: WindowsHelper(); void setIcon(const HWND& inHandle); private: PBYTE getIconDirectory(const int& inResourceId); HICON getIconFromIconDirectory(const PBYTE& inIconDirectory, const uint& inSize); HICON m_hIcon32; HICON m_hIcon16; }; #endif // WINDOWS_HELPER_HPP ================================================ FILE: app/src/main/env/.all.mk ================================================ MAX_PARALLEL_JOBS := 8 CLEAN_OUTPUT := true DUMP_ASSEMBLY := false _CFLAGS_STD := -std=c++17 _CFLAGS_WARNINGS := -Wall _CFLAGS_OTHER := -fdiagnostics-color=always CFLAGS := $(_CFLAGS_STD) $(_CFLAGS_WARNINGS) $(_CFLAGS_OTHER) LINK_LIBRARIES := \ sfml-graphics \ sfml-audio \ sfml-network \ sfml-window \ sfml-system PRECOMPILED_HEADER := PCH PRODUCTION_FOLDER := build PRODUCTION_EXCLUDE := \ *.psd \ *.rar \ *.7z \ Thumbs.db \ .DS_Store PRODUCTION_DEPENDENCIES := \ assets ================================================ FILE: app/src/main/env/.debug.mk ================================================ CFLAGS := -g -Og $(CFLAGS) -pg BUILD_FLAGS := \ -pg BUILD_MACROS := \ _DEBUG ================================================ FILE: app/src/main/env/.release.mk ================================================ CFLAGS := -O2 $(CFLAGS) ================================================ FILE: app/src/main/env/linux/exec.desktop ================================================ [Desktop Entry] Version=1.0 Type=Application Categories=Game;Application; Terminal=false Exec=${NAME} Path=${Path} Name=${PRODUCTION_LINUX_APP_NAME} Comment=${PRODUCTION_LINUX_APP_COMMENT} Icon=${PRODUCTION_LINUX_ICON} ================================================ FILE: app/src/main/env/linux.all.mk ================================================ CC := g++ LIB_DIRS := \ /usr/local/lib INCLUDE_DIRS := \ /usr/local/include BUILD_FLAGS := \ $(BUILD_FLAGS) \ -pthread LINK_LIBRARIES := \ $(LINK_LIBRARIES) \ X11 PRODUCTION_LINUX_ICON := icon PRODUCTION_LINUX_APP_NAME := Game PRODUCTION_LINUX_APP_COMMENT := Game ================================================ FILE: app/src/main/env/osx/Info.plist.json ================================================ { "CFBundleName": "${PRODUCTION_MACOS_BUNDLE_NAME}", "CFBundleDisplayName": "${PRODUCTION_MACOS_BUNDLE_DISPLAY_NAME}", "CFBundleIdentifier": "com.${PRODUCTION_MACOS_BUNDLE_DEVELOPER}.${PRODUCTION_MACOS_BUNDLE_NAME}", "CFBundleVersion": "0.0.1.1", "CFBundleDevelopmentRegion": "en", "CFBundleInfoDictionaryVersion": "6.0", "CFBundlePackageType": "APPL", "CFBundleSignature": "????", "CFBundleExecutable": "${NAME}", "CFBundleIconFile": "${MACOS_ICON}", "NSHighResolutionCapable": true } ================================================ FILE: app/src/main/env/osx/dmg.applescript ================================================ set appName to system attribute "appName" set appNameExt to appName & ".app" tell application "Finder" tell disk appName open set current view of container window to icon view set toolbar visible of container window to false set statusbar visible of container window to false set the bounds of container window to {400, 100, 1060, 500} set viewOptions to the icon view options of container window set arrangement of viewOptions to not arranged set icon size of viewOptions to 168 set background picture of viewOptions to file ".background:background.tiff" set position of item appNameExt of container window to {180, 182} set position of item "Applications" of container window to {480, 182} set position of item ".background" of container window to {180, 582} -- set position of item ".fseventsd" of container window to {480, 582} close update without registering applications delay 2 end tell end tell ================================================ FILE: app/src/main/env/osx.all.mk ================================================ CC := clang++ CFLAGS := $(CFLAGS:-s=) LIB_DIRS := \ /usr/local/lib INCLUDE_DIRS := \ /usr/local/include BUILD_FLAGS := MACOS_FRAMEWORK_PATHS := \ /Library/Frameworks # Name, no extension (eg. CoreFoundation, ogg) MACOS_FRAMEWORKS := \ CoreFoundation # Icon .png PRODUCTION_MACOS_ICON := icon PRODUCTION_DEPENDENCIES := \ $(PRODUCTION_DEPENDENCIES) PRODUCTION_MACOS_BUNDLE_DEVELOPER := developer PRODUCTION_MACOS_BUNDLE_DISPLAY_NAME := Game PRODUCTION_MACOS_BUNDLE_NAME := Game PRODUCTION_MACOS_MAKE_DMG := true PRODUCTION_MACOS_BACKGROUND := dmg-background PRODUCTION_MACOS_DYLIBS := \ /usr/local/lib/libsfml-graphics.2.5 \ /usr/local/lib/libsfml-audio.2.5 \ /usr/local/lib/libsfml-network.2.5 \ /usr/local/lib/libsfml-window.2.5 \ /usr/local/lib/libsfml-system.2.5 # Path, no extension (eg. /Library/Frameworks/ogg) PRODUCTION_MACOS_FRAMEWORKS := ================================================ FILE: app/src/main/env/osx.debug.mk ================================================ CFLAGS := -g $(_CFLAGS_STD) $(_CFLAGS_WARNINGS) $(_CFLAGS_OTHER) BUILD_FLAGS := $(BUILD_FLAGS:-pg=) ================================================ FILE: app/src/main/env/rpi.release.mk ================================================ CC := g++-8.1.0 MAX_PARALLEL_JOBS := 4 LIB_DIRS := \ /usr/local/gcc-8.1.0/lib \ /usr/local/lib INCLUDE_DIRS := \ /usr/local/gcc-8.1.0/include \ /usr/local/include BUILD_DEPENDENCIES := LINK_LIBRARIES := \ $(LINK_LIBRARIES) \ X11 BUILD_FLAGS := \ -pthread ================================================ FILE: app/src/main/env/windows/win-make_icon_from256.sh ================================================ #!/bin/bash echo if [[ $OSTYPE == 'msys' || $OSTYPE == 'win32' ]]; then if hash magick 2>/dev/null; then read -p "Icon Prefix (\"icon_256\"): " icon_prefix if [[ $icon_prefix == '' ]]; then icon_prefix='icon_256' fi output_file=app cmd="magick convert ${icon_prefix}.png -define icon:auto-resize=256,96,64,48,32,24,20,16 icon.ico" echo "$cmd" $cmd echo echo "Successfully created $output_file.ico!" else echo 'This script requires the "ImageMagick" command-line utility' echo 'Download from: https://www.imagemagick.org/script/download.php#windows' echo '(Install to default location)' fi else echo 'This script should only run in win32' fi echo read -rsn1 -p"Press any key to continue";echo ================================================ FILE: app/src/main/env/windows/win-make_icon_from_all.sh ================================================ #!/bin/bash echo if [[ $OSTYPE == 'msys' || $OSTYPE == 'win32' ]]; then if hash magick 2>/dev/null; then read -p "Icon Prefix (\"icon\"): " icon_prefix if [[ $icon_prefix == '' ]]; then icon_prefix='icon' fi output_file=icon cmd="magick convert ${icon_prefix}_256.png ${icon_prefix}_48.png ${icon_prefix}_32.png ${icon_prefix}_16.png $output_file.ico" echo "$cmd" $cmd echo echo "Successfully created $output_file.ico!" else echo 'This script requires the "ImageMagick" command-line utility' echo 'Download from: https://www.imagemagick.org/script/download.php#windows' echo '(Install to default location)' fi else echo 'This script should only run in win32' fi echo read -rsn1 -p"Press any key to continue";echo ================================================ FILE: app/src/main/env/windows.all.mk ================================================ CC := g++.exe _MINGW := C:/mingw32/bin _SFML := C:/SFML _SFML_BIN := $(_SFML)/bin LIB_DIRS := \ $(_SFML)/lib INCLUDE_DIRS := \ $(_SFML)/include BUILD_DEPENDENCIES := \ $(_SFML_BIN)/openal32.dll PRODUCTION_DEPENDENCIES := \ $(PRODUCTION_DEPENDENCIES) \ $(_MINGW)/libgcc_s_dw2-1.dll \ $(_MINGW)/libstdc++-6.dll \ $(_MINGW)/libwinpthread-1.dll \ $(_SFML_BIN)/openal32.dll \ $(_SFML_BIN)/sfml-audio-2.dll \ $(_SFML_BIN)/sfml-graphics-2.dll \ $(_SFML_BIN)/sfml-network-2.dll \ $(_SFML_BIN)/sfml-system-2.dll \ $(_SFML_BIN)/sfml-window-2.dll ================================================ FILE: app/src/main/env/windows.debug.mk ================================================ LINK_LIBRARIES := \ Ole32 \ comdlg32 \ sfml-graphics-d \ sfml-audio-d \ sfml-network-d \ sfml-window-d \ sfml-system-d ================================================ FILE: app/src/main/env/windows.release.mk ================================================ BUILD_FLAGS := \ -mwindows ================================================ FILE: app/src/main/external/SFML/include/SFML/Audio/AlResource.hpp ================================================ //////////////////////////////////////////////////////////// // // SFML - Simple and Fast Multimedia Library // Copyright (C) 2007-2023 Laurent Gomila (laurent@sfml-dev.org) // // This software is provided 'as-is', without any express or implied warranty. // In no event will the authors be held liable for any damages arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it freely, // subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; // you must not claim that you wrote the original software. // If you use this software in a product, an acknowledgment // in the product documentation would be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, // and must not be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source distribution. // //////////////////////////////////////////////////////////// #ifndef SFML_ALRESOURCE_HPP #define SFML_ALRESOURCE_HPP //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// #include namespace sf { //////////////////////////////////////////////////////////// /// \brief Base class for classes that require an OpenAL context /// //////////////////////////////////////////////////////////// class SFML_AUDIO_API AlResource { protected: //////////////////////////////////////////////////////////// /// \brief Default constructor /// //////////////////////////////////////////////////////////// AlResource(); //////////////////////////////////////////////////////////// /// \brief Destructor /// //////////////////////////////////////////////////////////// ~AlResource(); }; } // namespace sf #endif // SFML_ALRESOURCE_HPP //////////////////////////////////////////////////////////// /// \class sf::AlResource /// \ingroup audio /// /// This class is for internal use only, it must be the base /// of every class that requires a valid OpenAL context in /// order to work. /// //////////////////////////////////////////////////////////// ================================================ FILE: app/src/main/external/SFML/include/SFML/Audio/Export.hpp ================================================ //////////////////////////////////////////////////////////// // // SFML - Simple and Fast Multimedia Library // Copyright (C) 2007-2023 Laurent Gomila (laurent@sfml-dev.org) // // This software is provided 'as-is', without any express or implied warranty. // In no event will the authors be held liable for any damages arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it freely, // subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; // you must not claim that you wrote the original software. // If you use this software in a product, an acknowledgment // in the product documentation would be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, // and must not be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source distribution. // //////////////////////////////////////////////////////////// #ifndef SFML_AUDIO_EXPORT_HPP #define SFML_AUDIO_EXPORT_HPP //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// #include //////////////////////////////////////////////////////////// // Define portable import / export macros //////////////////////////////////////////////////////////// #if defined(SFML_AUDIO_EXPORTS) #define SFML_AUDIO_API SFML_API_EXPORT #else #define SFML_AUDIO_API SFML_API_IMPORT #endif #endif // SFML_AUDIO_EXPORT_HPP ================================================ FILE: app/src/main/external/SFML/include/SFML/Audio/InputSoundFile.hpp ================================================ //////////////////////////////////////////////////////////// // // SFML - Simple and Fast Multimedia Library // Copyright (C) 2007-2023 Laurent Gomila (laurent@sfml-dev.org) // // This software is provided 'as-is', without any express or implied warranty. // In no event will the authors be held liable for any damages arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it freely, // subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; // you must not claim that you wrote the original software. // If you use this software in a product, an acknowledgment // in the product documentation would be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, // and must not be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source distribution. // //////////////////////////////////////////////////////////// #ifndef SFML_INPUTSOUNDFILE_HPP #define SFML_INPUTSOUNDFILE_HPP //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// #include #include #include #include #include namespace sf { class InputStream; class SoundFileReader; //////////////////////////////////////////////////////////// /// \brief Provide read access to sound files /// //////////////////////////////////////////////////////////// class SFML_AUDIO_API InputSoundFile : NonCopyable { public: //////////////////////////////////////////////////////////// /// \brief Default constructor /// //////////////////////////////////////////////////////////// InputSoundFile(); //////////////////////////////////////////////////////////// /// \brief Destructor /// //////////////////////////////////////////////////////////// ~InputSoundFile(); //////////////////////////////////////////////////////////// /// \brief Open a sound file from the disk for reading /// /// The supported audio formats are: WAV (PCM only), OGG/Vorbis, FLAC, MP3. /// The supported sample sizes for FLAC and WAV are 8, 16, 24 and 32 bit. /// /// Because of minimp3_ex limitation, for MP3 files with big (>16kb) APEv2 tag, /// it may not be properly removed, tag data will be treated as MP3 data /// and there is a low chance of garbage decoded at the end of file. /// See also: https://github.com/lieff/minimp3 /// /// \param filename Path of the sound file to load /// /// \return True if the file was successfully opened /// //////////////////////////////////////////////////////////// bool openFromFile(const std::string& filename); //////////////////////////////////////////////////////////// /// \brief Open a sound file in memory for reading /// /// The supported audio formats are: WAV (PCM only), OGG/Vorbis, FLAC. /// The supported sample sizes for FLAC and WAV are 8, 16, 24 and 32 bit. /// /// \param data Pointer to the file data in memory /// \param sizeInBytes Size of the data to load, in bytes /// /// \return True if the file was successfully opened /// //////////////////////////////////////////////////////////// bool openFromMemory(const void* data, std::size_t sizeInBytes); //////////////////////////////////////////////////////////// /// \brief Open a sound file from a custom stream for reading /// /// The supported audio formats are: WAV (PCM only), OGG/Vorbis, FLAC. /// The supported sample sizes for FLAC and WAV are 8, 16, 24 and 32 bit. /// /// \param stream Source stream to read from /// /// \return True if the file was successfully opened /// //////////////////////////////////////////////////////////// bool openFromStream(InputStream& stream); //////////////////////////////////////////////////////////// /// \brief Get the total number of audio samples in the file /// /// \return Number of samples /// //////////////////////////////////////////////////////////// Uint64 getSampleCount() const; //////////////////////////////////////////////////////////// /// \brief Get the number of channels used by the sound /// /// \return Number of channels (1 = mono, 2 = stereo) /// //////////////////////////////////////////////////////////// unsigned int getChannelCount() const; //////////////////////////////////////////////////////////// /// \brief Get the sample rate of the sound /// /// \return Sample rate, in samples per second /// //////////////////////////////////////////////////////////// unsigned int getSampleRate() const; //////////////////////////////////////////////////////////// /// \brief Get the total duration of the sound file /// /// This function is provided for convenience, the duration is /// deduced from the other sound file attributes. /// /// \return Duration of the sound file /// //////////////////////////////////////////////////////////// Time getDuration() const; //////////////////////////////////////////////////////////// /// \brief Get the read offset of the file in time /// /// \return Time position /// //////////////////////////////////////////////////////////// Time getTimeOffset() const; //////////////////////////////////////////////////////////// /// \brief Get the read offset of the file in samples /// /// \return Sample position /// //////////////////////////////////////////////////////////// Uint64 getSampleOffset() const; //////////////////////////////////////////////////////////// /// \brief Change the current read position to the given sample offset /// /// This function takes a sample offset to provide maximum /// precision. If you need to jump to a given time, use the /// other overload. /// /// The sample offset takes the channels into account. /// If you have a time offset instead, you can easily find /// the corresponding sample offset with the following formula: /// `timeInSeconds * sampleRate * channelCount` /// If the given offset exceeds to total number of samples, /// this function jumps to the end of the sound file. /// /// \param sampleOffset Index of the sample to jump to, relative to the beginning /// //////////////////////////////////////////////////////////// void seek(Uint64 sampleOffset); //////////////////////////////////////////////////////////// /// \brief Change the current read position to the given time offset /// /// Using a time offset is handy but imprecise. If you need an accurate /// result, consider using the overload which takes a sample offset. /// /// If the given time exceeds to total duration, this function jumps /// to the end of the sound file. /// /// \param timeOffset Time to jump to, relative to the beginning /// //////////////////////////////////////////////////////////// void seek(Time timeOffset); //////////////////////////////////////////////////////////// /// \brief Read audio samples from the open file /// /// \param samples Pointer to the sample array to fill /// \param maxCount Maximum number of samples to read /// /// \return Number of samples actually read (may be less than \a maxCount) /// //////////////////////////////////////////////////////////// Uint64 read(Int16* samples, Uint64 maxCount); //////////////////////////////////////////////////////////// /// \brief Close the current file /// //////////////////////////////////////////////////////////// void close(); private: //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// SoundFileReader* m_reader; //!< Reader that handles I/O on the file's format InputStream* m_stream; //!< Input stream used to access the file's data bool m_streamOwned; //!< Is the stream internal or external? Uint64 m_sampleOffset; //!< Sample Read Position Uint64 m_sampleCount; //!< Total number of samples in the file unsigned int m_channelCount; //!< Number of channels of the sound unsigned int m_sampleRate; //!< Number of samples per second }; } // namespace sf #endif // SFML_INPUTSOUNDFILE_HPP //////////////////////////////////////////////////////////// /// \class sf::InputSoundFile /// \ingroup audio /// /// This class decodes audio samples from a sound file. It is /// used internally by higher-level classes such as sf::SoundBuffer /// and sf::Music, but can also be useful if you want to process /// or analyze audio files without playing them, or if you want to /// implement your own version of sf::Music with more specific /// features. /// /// Usage example: /// \code /// // Open a sound file /// sf::InputSoundFile file; /// if (!file.openFromFile("music.ogg")) /// /* error */; /// /// // Print the sound attributes /// std::cout << "duration: " << file.getDuration().asSeconds() << std::endl; /// std::cout << "channels: " << file.getChannelCount() << std::endl; /// std::cout << "sample rate: " << file.getSampleRate() << std::endl; /// std::cout << "sample count: " << file.getSampleCount() << std::endl; /// /// // Read and process batches of samples until the end of file is reached /// sf::Int16 samples[1024]; /// sf::Uint64 count; /// do /// { /// count = file.read(samples, 1024); /// /// // process, analyze, play, convert, or whatever /// // you want to do with the samples... /// } /// while (count > 0); /// \endcode /// /// \see sf::SoundFileReader, sf::OutputSoundFile /// //////////////////////////////////////////////////////////// ================================================ FILE: app/src/main/external/SFML/include/SFML/Audio/Listener.hpp ================================================ //////////////////////////////////////////////////////////// // // SFML - Simple and Fast Multimedia Library // Copyright (C) 2007-2023 Laurent Gomila (laurent@sfml-dev.org) // // This software is provided 'as-is', without any express or implied warranty. // In no event will the authors be held liable for any damages arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it freely, // subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; // you must not claim that you wrote the original software. // If you use this software in a product, an acknowledgment // in the product documentation would be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, // and must not be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source distribution. // //////////////////////////////////////////////////////////// #ifndef SFML_LISTENER_HPP #define SFML_LISTENER_HPP //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// #include #include namespace sf { //////////////////////////////////////////////////////////// /// \brief The audio listener is the point in the scene /// from where all the sounds are heard /// //////////////////////////////////////////////////////////// class SFML_AUDIO_API Listener { public: //////////////////////////////////////////////////////////// /// \brief Change the global volume of all the sounds and musics /// /// The volume is a number between 0 and 100; it is combined with /// the individual volume of each sound / music. /// The default value for the volume is 100 (maximum). /// /// \param volume New global volume, in the range [0, 100] /// /// \see getGlobalVolume /// //////////////////////////////////////////////////////////// static void setGlobalVolume(float volume); //////////////////////////////////////////////////////////// /// \brief Get the current value of the global volume /// /// \return Current global volume, in the range [0, 100] /// /// \see setGlobalVolume /// //////////////////////////////////////////////////////////// static float getGlobalVolume(); //////////////////////////////////////////////////////////// /// \brief Set the position of the listener in the scene /// /// The default listener's position is (0, 0, 0). /// /// \param x X coordinate of the listener's position /// \param y Y coordinate of the listener's position /// \param z Z coordinate of the listener's position /// /// \see getPosition, setDirection /// //////////////////////////////////////////////////////////// static void setPosition(float x, float y, float z); //////////////////////////////////////////////////////////// /// \brief Set the position of the listener in the scene /// /// The default listener's position is (0, 0, 0). /// /// \param position New listener's position /// /// \see getPosition, setDirection /// //////////////////////////////////////////////////////////// static void setPosition(const Vector3f& position); //////////////////////////////////////////////////////////// /// \brief Get the current position of the listener in the scene /// /// \return Listener's position /// /// \see setPosition /// //////////////////////////////////////////////////////////// static Vector3f getPosition(); //////////////////////////////////////////////////////////// /// \brief Set the forward vector of the listener in the scene /// /// The direction (also called "at vector") is the vector /// pointing forward from the listener's perspective. Together /// with the up vector, it defines the 3D orientation of the /// listener in the scene. The direction vector doesn't /// have to be normalized. /// The default listener's direction is (0, 0, -1). /// /// \param x X coordinate of the listener's direction /// \param y Y coordinate of the listener's direction /// \param z Z coordinate of the listener's direction /// /// \see getDirection, setUpVector, setPosition /// //////////////////////////////////////////////////////////// static void setDirection(float x, float y, float z); //////////////////////////////////////////////////////////// /// \brief Set the forward vector of the listener in the scene /// /// The direction (also called "at vector") is the vector /// pointing forward from the listener's perspective. Together /// with the up vector, it defines the 3D orientation of the /// listener in the scene. The direction vector doesn't /// have to be normalized. /// The default listener's direction is (0, 0, -1). /// /// \param direction New listener's direction /// /// \see getDirection, setUpVector, setPosition /// //////////////////////////////////////////////////////////// static void setDirection(const Vector3f& direction); //////////////////////////////////////////////////////////// /// \brief Get the current forward vector of the listener in the scene /// /// \return Listener's forward vector (not normalized) /// /// \see setDirection /// //////////////////////////////////////////////////////////// static Vector3f getDirection(); //////////////////////////////////////////////////////////// /// \brief Set the upward vector of the listener in the scene /// /// The up vector is the vector that points upward from the /// listener's perspective. Together with the direction, it /// defines the 3D orientation of the listener in the scene. /// The up vector doesn't have to be normalized. /// The default listener's up vector is (0, 1, 0). It is usually /// not necessary to change it, especially in 2D scenarios. /// /// \param x X coordinate of the listener's up vector /// \param y Y coordinate of the listener's up vector /// \param z Z coordinate of the listener's up vector /// /// \see getUpVector, setDirection, setPosition /// //////////////////////////////////////////////////////////// static void setUpVector(float x, float y, float z); //////////////////////////////////////////////////////////// /// \brief Set the upward vector of the listener in the scene /// /// The up vector is the vector that points upward from the /// listener's perspective. Together with the direction, it /// defines the 3D orientation of the listener in the scene. /// The up vector doesn't have to be normalized. /// The default listener's up vector is (0, 1, 0). It is usually /// not necessary to change it, especially in 2D scenarios. /// /// \param upVector New listener's up vector /// /// \see getUpVector, setDirection, setPosition /// //////////////////////////////////////////////////////////// static void setUpVector(const Vector3f& upVector); //////////////////////////////////////////////////////////// /// \brief Get the current upward vector of the listener in the scene /// /// \return Listener's upward vector (not normalized) /// /// \see setUpVector /// //////////////////////////////////////////////////////////// static Vector3f getUpVector(); }; } // namespace sf #endif // SFML_LISTENER_HPP //////////////////////////////////////////////////////////// /// \class sf::Listener /// \ingroup audio /// /// The audio listener defines the global properties of the /// audio environment, it defines where and how sounds and musics /// are heard. If sf::View is the eyes of the user, then sf::Listener /// is his ears (by the way, they are often linked together -- /// same position, orientation, etc.). /// /// sf::Listener is a simple interface, which allows to setup the /// listener in the 3D audio environment (position, direction and /// up vector), and to adjust the global volume. /// /// Because the listener is unique in the scene, sf::Listener only /// contains static functions and doesn't have to be instantiated. /// /// Usage example: /// \code /// // Move the listener to the position (1, 0, -5) /// sf::Listener::setPosition(1, 0, -5); /// /// // Make it face the right axis (1, 0, 0) /// sf::Listener::setDirection(1, 0, 0); /// /// // Reduce the global volume /// sf::Listener::setGlobalVolume(50); /// \endcode /// //////////////////////////////////////////////////////////// ================================================ FILE: app/src/main/external/SFML/include/SFML/Audio/Music.hpp ================================================ //////////////////////////////////////////////////////////// // // SFML - Simple and Fast Multimedia Library // Copyright (C) 2007-2023 Laurent Gomila (laurent@sfml-dev.org) // // This software is provided 'as-is', without any express or implied warranty. // In no event will the authors be held liable for any damages arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it freely, // subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; // you must not claim that you wrote the original software. // If you use this software in a product, an acknowledgment // in the product documentation would be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, // and must not be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source distribution. // //////////////////////////////////////////////////////////// #ifndef SFML_MUSIC_HPP #define SFML_MUSIC_HPP //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// #include #include #include #include #include #include #include namespace sf { class InputStream; //////////////////////////////////////////////////////////// /// \brief Streamed music played from an audio file /// //////////////////////////////////////////////////////////// class SFML_AUDIO_API Music : public SoundStream { public: //////////////////////////////////////////////////////////// /// \brief Structure defining a time range using the template type /// //////////////////////////////////////////////////////////// template struct Span { //////////////////////////////////////////////////////////// /// \brief Default constructor /// //////////////////////////////////////////////////////////// Span() { } //////////////////////////////////////////////////////////// /// \brief Initialization constructor /// /// \param off Initial Offset /// \param len Initial Length /// //////////////////////////////////////////////////////////// Span(T off, T len): offset(off), length(len) { } T offset; //!< The beginning offset of the time range T length; //!< The length of the time range }; // Define the relevant Span types typedef Span